summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/android')
-rw-r--r--src/plugins/platforms/android/CMakeLists.txt40
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.cpp882
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.h127
-rw-r--r--src/plugins/platforms/android/androiddeadlockprotector.cpp43
-rw-r--r--src/plugins/platforms/android/androiddeadlockprotector.h54
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.cpp438
-rw-r--r--src/plugins/platforms/android/androidjniaccessibility.h51
-rw-r--r--src/plugins/platforms/android/androidjniclipboard.cpp134
-rw-r--r--src/plugins/platforms/android/androidjniclipboard.h63
-rw-r--r--src/plugins/platforms/android/androidjniinput.cpp411
-rw-r--r--src/plugins/platforms/android/androidjniinput.h71
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp620
-rw-r--r--src/plugins/platforms/android/androidjnimain.h74
-rw-r--r--src/plugins/platforms/android/androidjnimenu.cpp114
-rw-r--r--src/plugins/platforms/android/androidjnimenu.h45
-rw-r--r--src/plugins/platforms/android/androidsurfaceclient.h60
-rw-r--r--src/plugins/platforms/android/androidwindowembedding.cpp70
-rw-r--r--src/plugins/platforms/android/androidwindowembedding.h41
-rw-r--r--src/plugins/platforms/android/extract-dummy.cpp46
-rw-r--r--src/plugins/platforms/android/extract.cpp61
-rw-r--r--src/plugins/platforms/android/main.cpp44
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp223
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.h43
-rw-r--r--src/plugins/platforms/android/qandroideventdispatcher.cpp48
-rw-r--r--src/plugins/platforms/android/qandroideventdispatcher.h40
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.cpp363
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h54
-rw-r--r--src/plugins/platforms/android/qandroidplatformaccessibility.cpp54
-rw-r--r--src/plugins/platforms/android/qandroidplatformaccessibility.h41
-rw-r--r--src/plugins/platforms/android/qandroidplatformbackingstore.cpp88
-rw-r--r--src/plugins/platforms/android/qandroidplatformbackingstore.h65
-rw-r--r--src/plugins/platforms/android/qandroidplatformclipboard.cpp123
-rw-r--r--src/plugins/platforms/android/qandroidplatformclipboard.h56
-rw-r--r--src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp128
-rw-r--r--src/plugins/platforms/android/qandroidplatformdialoghelpers.h55
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp92
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.h50
-rw-r--r--src/plugins/platforms/android/qandroidplatformfontdatabase.cpp94
-rw-r--r--src/plugins/platforms/android/qandroidplatformfontdatabase.h41
-rw-r--r--src/plugins/platforms/android/qandroidplatformforeignwindow.cpp129
-rw-r--r--src/plugins/platforms/android/qandroidplatformforeignwindow.h54
-rw-r--r--src/plugins/platforms/android/qandroidplatformiconengine.cpp616
-rw-r--r--src/plugins/platforms/android/qandroidplatformiconengine.h44
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.cpp293
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.h85
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenu.cpp44
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenu.h40
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenubar.cpp40
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenubar.h40
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenuitem.cpp40
-rw-r--r--src/plugins/platforms/android/qandroidplatformmenuitem.h40
-rw-r--r--src/plugins/platforms/android/qandroidplatformoffscreensurface.cpp40
-rw-r--r--src/plugins/platforms/android/qandroidplatformoffscreensurface.h40
-rw-r--r--src/plugins/platforms/android/qandroidplatformopenglcontext.cpp42
-rw-r--r--src/plugins/platforms/android/qandroidplatformopenglcontext.h42
-rw-r--r--src/plugins/platforms/android/qandroidplatformopenglwindow.cpp114
-rw-r--r--src/plugins/platforms/android/qandroidplatformopenglwindow.h51
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.cpp459
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.h99
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.cpp137
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.h54
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.cpp240
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.h56
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp40
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkaninstance.h40
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp90
-rw-r--r--src/plugins/platforms/android/qandroidplatformvulkanwindow.h51
-rw-r--r--src/plugins/platforms/android/qandroidplatformwindow.cpp310
-rw-r--r--src/plugins/platforms/android/qandroidplatformwindow.h110
-rw-r--r--src/plugins/platforms/android/qandroidsystemlocale.cpp99
-rw-r--r--src/plugins/platforms/android/qandroidsystemlocale.h43
71 files changed, 4316 insertions, 4453 deletions
diff --git a/src/plugins/platforms/android/CMakeLists.txt b/src/plugins/platforms/android/CMakeLists.txt
index af9b7429b6..d5a275a76c 100644
--- a/src/plugins/platforms/android/CMakeLists.txt
+++ b/src/plugins/platforms/android/CMakeLists.txt
@@ -1,34 +1,33 @@
-# Generated from android.pro.
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## QAndroidIntegrationPlugin Plugin:
#####################################################################
-qt_find_package(EGL) # special case
+qt_find_package(EGL)
qt_internal_add_plugin(QAndroidIntegrationPlugin
OUTPUT_NAME qtforandroid
- TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES android # special case
+ PLUGIN_TYPE platforms
+ DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES android
SOURCES
androidcontentfileengine.cpp androidcontentfileengine.h
- androiddeadlockprotector.cpp androiddeadlockprotector.h
+ androiddeadlockprotector.h
androidjniaccessibility.cpp androidjniaccessibility.h
- androidjniclipboard.cpp androidjniclipboard.h
androidjniinput.cpp androidjniinput.h
androidjnimain.cpp androidjnimain.h
androidjnimenu.cpp androidjnimenu.h
- androidsurfaceclient.h
main.cpp
qandroidassetsfileenginehandler.cpp qandroidassetsfileenginehandler.h
qandroideventdispatcher.cpp qandroideventdispatcher.h
qandroidinputcontext.cpp qandroidinputcontext.h
qandroidplatformaccessibility.cpp qandroidplatformaccessibility.h
- qandroidplatformbackingstore.cpp qandroidplatformbackingstore.h
qandroidplatformclipboard.cpp qandroidplatformclipboard.h
qandroidplatformdialoghelpers.cpp qandroidplatformdialoghelpers.h
qandroidplatformfiledialoghelper.cpp qandroidplatformfiledialoghelper.h
qandroidplatformfontdatabase.cpp qandroidplatformfontdatabase.h
qandroidplatformforeignwindow.cpp qandroidplatformforeignwindow.h
+ qandroidplatformiconengine.cpp qandroidplatformiconengine.h
qandroidplatformintegration.cpp qandroidplatformintegration.h
qandroidplatformmenu.cpp qandroidplatformmenu.h
qandroidplatformmenubar.cpp qandroidplatformmenubar.h
@@ -41,11 +40,22 @@ qt_internal_add_plugin(QAndroidIntegrationPlugin
qandroidplatformtheme.cpp qandroidplatformtheme.h
qandroidplatformwindow.cpp qandroidplatformwindow.h
qandroidsystemlocale.cpp qandroidsystemlocale.h
- DEFINES
- QT_USE_QSTRINGBUILDER
+ androidwindowembedding.cpp androidwindowembedding.h
+ NO_UNITY_BUILD_SOURCES
+ # Conflicting symbols and macros with androidjnimain.cpp
+ # TODO: Unify the usage of FIND_AND_CHECK_CLASS, and similar
+ # macros. Q_JNI_FIND_AND_CHECK_CLASS in `qjnihelpers_p.h`
+ # seems to be doing most of the work already.
+ androidjnimenu.cpp
+ qandroidinputcontext.cpp
+ androidjniaccessibility.cpp
+ qandroidplatformdialoghelpers.cpp
+ # Conflicting JNI classes, and types
+ androidcontentfileengine.cpp
+ qandroidplatformintegration.cpp
INCLUDE_DIRECTORIES
${CMAKE_CURRENT_SOURCE_DIR}
- ${QT_SOURCE_TREE}/src/3rdparty/android
+ ${QtBase_SOURCE_DIR}/src/3rdparty/android
LIBRARIES
EGL::EGL
Qt::Core
@@ -54,12 +64,9 @@ qt_internal_add_plugin(QAndroidIntegrationPlugin
Qt::GuiPrivate
android
jnigraphics
- EGL::EGL # special case
+ EGL::EGL
)
-#### Keys ignored in scope 1:.:.:android.pro:<TRUE>:
-# OTHER_FILES = "$$PWD/android.json"
-
## Scopes:
#####################################################################
@@ -77,4 +84,7 @@ qt_internal_extend_target(QAndroidIntegrationPlugin CONDITION QT_FEATURE_vulkan
SOURCES
qandroidplatformvulkaninstance.cpp qandroidplatformvulkaninstance.h
qandroidplatformvulkanwindow.cpp qandroidplatformvulkanwindow.h
+ NO_UNITY_BUILD_SOURCES
+ # To avoid undefined symbols due to missing VK_USE_PLATFORM_ANDROID_KHR
+ qandroidplatformvulkaninstance.cpp qandroidplatformvulkanwindow.cpp
)
diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp
index 2c47b71c88..db6c601f33 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -1,120 +1,229 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Volker Krause <vkrause@kde.org>
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Volker Krause <vkrause@kde.org>
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidcontentfileengine.h"
#include <QtCore/qcoreapplication.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjniobject.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmimedatabase.h>
-#include <QDebug>
+QT_BEGIN_NAMESPACE
using namespace QNativeInterface;
+using namespace Qt::StringLiterals;
-AndroidContentFileEngine::AndroidContentFileEngine(const QString &f)
- : m_file(f)
+Q_DECLARE_JNI_CLASS(ContentResolverType, "android/content/ContentResolver");
+Q_DECLARE_JNI_CLASS(UriType, "android/net/Uri");
+Q_DECLARE_JNI_CLASS(Uri, "android/net/Uri");
+Q_DECLARE_JNI_CLASS(ParcelFileDescriptorType, "android/os/ParcelFileDescriptor");
+Q_DECLARE_JNI_CLASS(CursorType, "android/database/Cursor");
+Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;");
+
+static QJniObject &contentResolverInstance()
+{
+ static QJniObject contentResolver;
+ if (!contentResolver.isValid()) {
+ contentResolver = QJniObject(QNativeInterface::QAndroidApplication::context())
+ .callMethod<QtJniTypes::ContentResolverType>("getContentResolver");
+ }
+
+ return contentResolver;
+}
+
+AndroidContentFileEngine::AndroidContentFileEngine(const QString &filename)
+ : m_initialFile(filename),
+ m_documentFile(DocumentFile::parseFromAnyUri(filename))
{
- setFileName(f);
+ setFileName(filename);
}
-bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode)
+bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode,
+ std::optional<QFile::Permissions> permissions)
{
+ Q_UNUSED(permissions);
QString openModeStr;
if (openMode & QFileDevice::ReadOnly) {
- openModeStr += QLatin1Char('r');
+ openModeStr += u'r';
}
if (openMode & QFileDevice::WriteOnly) {
- openModeStr += QLatin1Char('w');
+ openModeStr += u'w';
+ if (!m_documentFile->exists()) {
+ if (QUrl(m_initialFile).path().startsWith("/tree/"_L1)) {
+ const int lastSeparatorIndex = m_initialFile.lastIndexOf('/');
+ const QString fileName = m_initialFile.mid(lastSeparatorIndex + 1);
+
+ QString mimeType;
+ const auto mimeTypes = QMimeDatabase().mimeTypesForFileName(fileName);
+ if (!mimeTypes.empty())
+ mimeType = mimeTypes.first().name();
+ else
+ mimeType = "application/octet-stream";
+
+ if (m_documentFile->parent()) {
+ auto createdFile = m_documentFile->parent()->createFile(mimeType, fileName);
+ if (createdFile)
+ m_documentFile = createdFile;
+ }
+ } else {
+ qWarning() << "open(): non-existent content URI with a document type provided";
+ }
+ }
}
if (openMode & QFileDevice::Truncate) {
- openModeStr += QLatin1Char('t');
+ openModeStr += u't';
} else if (openMode & QFileDevice::Append) {
- openModeStr += QLatin1Char('a');
+ openModeStr += u'a';
}
- const auto fd = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
- "openFdForContentUrl",
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I",
- QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object(),
- QJniObject::fromString(openModeStr).object());
+ m_pfd = contentResolverInstance().callMethod<
+ QtJniTypes::ParcelFileDescriptorType, QtJniTypes::UriType, jstring>(
+ "openFileDescriptor",
+ m_documentFile->uri().object(),
+ QJniObject::fromString(openModeStr).object<jstring>());
+
+ if (!m_pfd.isValid())
+ return false;
+
+ const auto fd = m_pfd.callMethod<jint>("getFd");
- if (fd < 0)
+ if (fd < 0) {
+ closeNativeFileDescriptor();
return false;
+ }
+
+ return QFSFileEngine::open(openMode, fd, QFile::DontCloseHandle);
+}
+
+bool AndroidContentFileEngine::close()
+{
+ closeNativeFileDescriptor();
+ return QFSFileEngine::close();
+}
- return QFSFileEngine::open(openMode, fd, QFile::AutoCloseHandle);
+void AndroidContentFileEngine::closeNativeFileDescriptor()
+{
+ if (m_pfd.isValid()) {
+ m_pfd.callMethod<void>("close");
+ m_pfd = QJniObject();
+ }
}
qint64 AndroidContentFileEngine::size() const
{
- const jlong size = QJniObject::callStaticMethod<jlong>(
- "org/qtproject/qt/android/QtNative", "getSize",
- "(Landroid/content/Context;Ljava/lang/String;)J", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- return (qint64)size;
+ return m_documentFile->length();
+}
+
+bool AndroidContentFileEngine::remove()
+{
+ return m_documentFile->remove();
+}
+
+bool AndroidContentFileEngine::rename(const QString &newName)
+{
+ if (m_documentFile->rename(newName)) {
+ m_initialFile = m_documentFile->uri().toString();
+ return true;
+ }
+ return false;
+}
+
+bool AndroidContentFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFileDevice::Permissions> permissions) const
+{
+ Q_UNUSED(permissions)
+
+ QString tmp = dirName;
+ tmp.remove(m_initialFile);
+
+ QStringList dirParts = tmp.split(u'/');
+ dirParts.removeAll("");
+
+ if (dirParts.isEmpty())
+ return false;
+
+ auto createdDir = m_documentFile;
+ bool allDirsCreated = true;
+ for (const auto &dir : dirParts) {
+ // Find if the sub-dir already exists and then don't re-create it
+ bool subDirExists = false;
+ for (const DocumentFilePtr &subDir : m_documentFile->listFiles()) {
+ if (dir == subDir->name() && subDir->isDirectory()) {
+ createdDir = subDir;
+ subDirExists = true;
+ }
+ }
+
+ if (!subDirExists) {
+ createdDir = createdDir->createDirectory(dir);
+ if (!createdDir) {
+ allDirsCreated = false;
+ break;
+ }
+ }
+
+ if (!createParentDirectories)
+ break;
+ }
+
+ return allDirsCreated;
+}
+
+bool AndroidContentFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
+{
+ if (recurseParentDirectories)
+ qWarning() << "rmpath(): Unsupported for Content URIs";
+
+ const QString dirFileName = QUrl(dirName).fileName();
+ bool deleted = false;
+ for (const DocumentFilePtr &dir : m_documentFile->listFiles()) {
+ if (dirFileName == dir->name() && dir->isDirectory()) {
+ deleted = dir->remove();
+ break;
+ }
+ }
+
+ return deleted;
+}
+
+QByteArray AndroidContentFileEngine::id() const
+{
+ return m_documentFile->id().toUtf8();
+}
+
+QDateTime AndroidContentFileEngine::fileTime(QFile::FileTime time) const
+{
+ switch (time) {
+ case QFile::FileModificationTime:
+ return m_documentFile->lastModified();
+ break;
+ default:
+ break;
+ }
+
+ return QDateTime();
}
AndroidContentFileEngine::FileFlags AndroidContentFileEngine::fileFlags(FileFlags type) const
{
- FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
FileFlags flags;
- const bool isDir = QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkIfDir",
- "(Landroid/content/Context;Ljava/lang/String;)Z", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- // If it is a directory then we know it exists so there is no reason to explicitly check
- const bool exists = isDir ? true : QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkFileExists",
- "(Landroid/content/Context;Ljava/lang/String;)Z", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- if (!exists && !isDir)
+ if (!m_documentFile->exists())
return flags;
- if (isDir) {
- flags = DirectoryType | commonFlags;
+
+ flags = ExistsFlag;
+ if (!m_documentFile->canRead())
+ return flags;
+
+ flags |= ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm;
+
+ if (m_documentFile->isDirectory()) {
+ flags |= DirectoryType;
} else {
- flags = FileType | commonFlags;
- const bool writable = QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkIfWritable",
- "(Landroid/content/Context;Ljava/lang/String;)Z", QAndroidApplication::context(),
- QJniObject::fromString(fileName(DefaultName)).object());
- if (writable)
+ flags |= FileType;
+ if (m_documentFile->canWrite())
flags |= WriteOwnerPerm|WriteUserPerm|WriteGroupPerm|WriteOtherPerm;
}
return type & flags;
@@ -129,42 +238,39 @@ QString AndroidContentFileEngine::fileName(FileName f) const
case DefaultName:
case AbsoluteName:
case CanonicalName:
- return m_file;
+ return m_documentFile->uri().toString();
case BaseName:
- {
- const int pos = m_file.lastIndexOf(QChar(QLatin1Char('/')));
- return m_file.mid(pos);
- }
+ return m_documentFile->name();
default:
- return QString();
+ break;
}
-}
-QAbstractFileEngine::Iterator *AndroidContentFileEngine::beginEntryList(QDir::Filters filters, const QStringList &filterNames)
-{
- return new AndroidContentFileEngineIterator(filters, filterNames);
+ return QString();
}
-QAbstractFileEngine::Iterator *AndroidContentFileEngine::endEntryList()
+QAbstractFileEngine::IteratorUniquePtr
+AndroidContentFileEngine::beginEntryList(const QString &path, QDir::Filters filters,
+ const QStringList &filterNames)
{
- return nullptr;
+ return std::make_unique<AndroidContentFileEngineIterator>(path, filters, filterNames);
}
AndroidContentFileEngineHandler::AndroidContentFileEngineHandler() = default;
AndroidContentFileEngineHandler::~AndroidContentFileEngineHandler() = default;
-QAbstractFileEngine* AndroidContentFileEngineHandler::create(const QString &fileName) const
+std::unique_ptr<QAbstractFileEngine>
+AndroidContentFileEngineHandler::create(const QString &fileName) const
{
- if (!fileName.startsWith(QLatin1String("content"))) {
- return nullptr;
- }
+ if (fileName.startsWith("content"_L1))
+ return std::make_unique<AndroidContentFileEngine>(fileName);
+
+ return {};
- return new AndroidContentFileEngine(fileName);
}
-AndroidContentFileEngineIterator::AndroidContentFileEngineIterator(QDir::Filters filters,
- const QStringList &filterNames)
- : QAbstractFileEngineIterator(filters, filterNames)
+AndroidContentFileEngineIterator::AndroidContentFileEngineIterator(
+ const QString &path, QDir::Filters filters, const QStringList &filterNames)
+ : QAbstractFileEngineIterator(path, filters, filterNames)
{
}
@@ -172,47 +278,557 @@ AndroidContentFileEngineIterator::~AndroidContentFileEngineIterator()
{
}
-QString AndroidContentFileEngineIterator::next()
+bool AndroidContentFileEngineIterator::advance()
{
- if (!hasNext())
- return QString();
- ++m_index;
- return currentFilePath();
-}
+ if (m_index == -1 && m_files.isEmpty()) {
+ const auto currentPath = path();
+ if (currentPath.isEmpty())
+ return false;
-bool AndroidContentFileEngineIterator::hasNext() const
-{
- if (m_index == -1) {
- if (path().isEmpty())
+ const auto iterDoc = DocumentFile::parseFromAnyUri(currentPath);
+ if (iterDoc->isDirectory())
+ for (const auto &doc : iterDoc->listFiles())
+ m_files.append(doc);
+ if (m_files.isEmpty())
return false;
- const bool isDir = QJniObject::callStaticMethod<jboolean>(
- "org/qtproject/qt/android/QtNative", "checkIfDir",
- "(Landroid/content/Context;Ljava/lang/String;)Z",
- QAndroidApplication::context(),
- QJniObject::fromString(path()).object());
- if (isDir) {
- QJniObject objArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative",
- "listContentsFromTreeUri",
- "(Landroid/content/Context;Ljava/lang/String;)[Ljava/lang/String;",
- QAndroidApplication::context(),
- QJniObject::fromString(path()).object());
- if (objArray.isValid()) {
- QJniEnvironment env;
- const jsize length = env->GetArrayLength(objArray.object<jarray>());
- for (int i = 0; i != length; ++i) {
- m_entries << QJniObject(env->GetObjectArrayElement(
- objArray.object<jobjectArray>(), i)).toString();
- }
- }
- }
m_index = 0;
+ return true;
+ }
+
+ if (m_index < m_files.size() - 1) {
+ ++m_index;
+ return true;
}
- return m_index < m_entries.size();
+
+ return false;
}
QString AndroidContentFileEngineIterator::currentFileName() const
{
- if (m_index <= 0 || m_index > m_entries.size())
+ if (m_index < 0 || m_index > m_files.size())
return QString();
- return m_entries.at(m_index - 1);
+ return m_files.at(m_index)->name();
+}
+
+QString AndroidContentFileEngineIterator::currentFilePath() const
+{
+ if (m_index < 0 || m_index > m_files.size())
+ return QString();
+ return m_files.at(m_index)->uri().toString();
+}
+
+// Start of Cursor
+
+class Cursor
+{
+public:
+ explicit Cursor(const QJniObject &object)
+ : m_object{object} { }
+
+ ~Cursor()
+ {
+ if (m_object.isValid())
+ m_object.callMethod<void>("close");
+ }
+
+ enum Type {
+ FIELD_TYPE_NULL = 0x00000000,
+ FIELD_TYPE_INTEGER = 0x00000001,
+ FIELD_TYPE_FLOAT = 0x00000002,
+ FIELD_TYPE_STRING = 0x00000003,
+ FIELD_TYPE_BLOB = 0x00000004
+ };
+
+ QVariant data(int columnIndex) const
+ {
+ int type = m_object.callMethod<jint>("getType", columnIndex);
+ switch (type) {
+ case FIELD_TYPE_NULL:
+ return {};
+ case FIELD_TYPE_INTEGER:
+ return QVariant::fromValue(m_object.callMethod<jlong>("getLong", columnIndex));
+ case FIELD_TYPE_FLOAT:
+ return QVariant::fromValue(m_object.callMethod<jdouble>("getDouble", columnIndex));
+ case FIELD_TYPE_STRING:
+ return QVariant::fromValue(m_object.callMethod<jstring>("getString",
+ columnIndex).toString());
+ case FIELD_TYPE_BLOB: {
+ auto blob = m_object.callMethod<jbyteArray>("getBlob", columnIndex);
+ QJniEnvironment env;
+ const auto blobArray = blob.object<jbyteArray>();
+ const int size = env->GetArrayLength(blobArray);
+ const auto byteArray = env->GetByteArrayElements(blobArray, nullptr);
+ QByteArray data{reinterpret_cast<const char *>(byteArray), size};
+ env->ReleaseByteArrayElements(blobArray, byteArray, 0);
+ return QVariant::fromValue(data);
+ }
+ }
+ return {};
+ }
+
+ static std::unique_ptr<Cursor> queryUri(const QJniObject &uri,
+ const QStringList &projection = {},
+ const QString &selection = {},
+ const QStringList &selectionArgs = {},
+ const QString &sortOrder = {})
+ {
+ auto cursor = contentResolverInstance().callMethod<QtJniTypes::CursorType>(
+ "query",
+ uri.object<QtJniTypes::UriType>(),
+ projection.isEmpty() ?
+ nullptr : fromStringList(projection).object<QtJniTypes::StringArray>(),
+ selection.isEmpty() ? nullptr : QJniObject::fromString(selection).object<jstring>(),
+ selectionArgs.isEmpty() ?
+ nullptr : fromStringList(selectionArgs).object<QtJniTypes::StringArray>(),
+ sortOrder.isEmpty() ? nullptr : QJniObject::fromString(sortOrder).object<jstring>());
+ if (!cursor.isValid())
+ return {};
+ return std::make_unique<Cursor>(cursor);
+ }
+
+ static QVariant queryColumn(const QJniObject &uri, const QString &column)
+ {
+ const auto query = queryUri(uri, {column});
+ if (!query)
+ return {};
+
+ if (query->rowCount() != 1 || query->columnCount() != 1)
+ return {};
+ query->moveToFirst();
+ return query->data(0);
+ }
+
+ bool isNull(int columnIndex) const
+ {
+ return m_object.callMethod<jboolean>("isNull", columnIndex);
+ }
+
+ int columnCount() const { return m_object.callMethod<jint>("getColumnCount"); }
+ int rowCount() const { return m_object.callMethod<jint>("getCount"); }
+ int row() const { return m_object.callMethod<jint>("getPosition"); }
+ bool isFirst() const { return m_object.callMethod<jboolean>("isFirst"); }
+ bool isLast() const { return m_object.callMethod<jboolean>("isLast"); }
+ bool moveToFirst() { return m_object.callMethod<jboolean>("moveToFirst"); }
+ bool moveToLast() { return m_object.callMethod<jboolean>("moveToLast"); }
+ bool moveToNext() { return m_object.callMethod<jboolean>("moveToNext"); }
+
+private:
+ static QJniObject fromStringList(const QStringList &list)
+ {
+ QJniEnvironment env;
+ auto array = env->NewObjectArray(list.size(), env.findClass("java/lang/String"), nullptr);
+ for (int i = 0; i < list.size(); ++i)
+ env->SetObjectArrayElement(array, i, QJniObject::fromString(list[i]).object());
+ return QJniObject::fromLocalRef(array);
+ }
+
+ QJniObject m_object;
+};
+
+// End of Cursor
+
+// Start of DocumentsContract
+
+Q_DECLARE_JNI_CLASS(DocumentsContract, "android/provider/DocumentsContract");
+
+/*!
+ *
+ * DocumentsContract Api.
+ * Check https://developer.android.com/reference/android/provider/DocumentsContract
+ * for more information.
+ *
+ * \note This does not implement all facilities of the native API.
+ *
+ */
+namespace DocumentsContract
+{
+
+namespace Document {
+const QLatin1String COLUMN_DISPLAY_NAME("_display_name");
+const QLatin1String COLUMN_DOCUMENT_ID("document_id");
+const QLatin1String COLUMN_FLAGS("flags");
+const QLatin1String COLUMN_LAST_MODIFIED("last_modified");
+const QLatin1String COLUMN_MIME_TYPE("mime_type");
+const QLatin1String COLUMN_SIZE("_size");
+
+constexpr int FLAG_DIR_SUPPORTS_CREATE = 0x00000008;
+constexpr int FLAG_SUPPORTS_DELETE = 0x00000004;
+constexpr int FLAG_SUPPORTS_MOVE = 0x00000100;
+constexpr int FLAG_SUPPORTS_RENAME = 0x00000040;
+constexpr int FLAG_SUPPORTS_WRITE = 0x00000002;
+constexpr int FLAG_VIRTUAL_DOCUMENT = 0x00000200;
+
+const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory");
+} // namespace Document
+
+QString documentId(const QJniObject &uri)
+{
+ return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "getDocumentId",
+ uri.object()).toString();
+}
+
+QString treeDocumentId(const QJniObject &uri)
+{
+ return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "getTreeDocumentId",
+ uri.object()).toString();
+}
+
+QJniObject buildChildDocumentsUriUsingTree(const QJniObject &uri, const QString &parentDocumentId)
+{
+ return QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "buildChildDocumentsUriUsingTree",
+ uri.object<QtJniTypes::UriType>(),
+ QJniObject::fromString(parentDocumentId).object<jstring>());
+
+}
+
+QJniObject buildDocumentUriUsingTree(const QJniObject &treeUri, const QString &documentId)
+{
+ return QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "buildDocumentUriUsingTree",
+ treeUri.object<QtJniTypes::UriType>(),
+ QJniObject::fromString(documentId).object<jstring>());
+}
+
+bool isDocumentUri(const QJniObject &uri)
+{
+ return QJniObject::callStaticMethod<jboolean>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "isDocumentUri",
+ QNativeInterface::QAndroidApplication::context(),
+ uri.object<QtJniTypes::UriType>());
+}
+
+bool isTreeUri(const QJniObject &uri)
+{
+ return QJniObject::callStaticMethod<jboolean>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "isTreeUri",
+ uri.object<QtJniTypes::UriType>());
+}
+
+QJniObject createDocument(const QJniObject &parentDocumentUri, const QString &mimeType,
+ const QString &displayName)
+{
+ return QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "createDocument",
+ contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
+ parentDocumentUri.object<QtJniTypes::UriType>(),
+ QJniObject::fromString(mimeType).object<jstring>(),
+ QJniObject::fromString(displayName).object<jstring>());
+}
+
+bool deleteDocument(const QJniObject &documentUri)
+{
+ const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
+ if (!(flags & Document::FLAG_SUPPORTS_DELETE))
+ return {};
+
+ return QJniObject::callStaticMethod<jboolean>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "deleteDocument",
+ contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
+ documentUri.object<QtJniTypes::UriType>());
+}
+
+QJniObject moveDocument(const QJniObject &sourceDocumentUri,
+ const QJniObject &sourceParentDocumentUri,
+ const QJniObject &targetParentDocumentUri)
+{
+ const int flags = Cursor::queryColumn(sourceDocumentUri, Document::COLUMN_FLAGS).toInt();
+ if (!(flags & Document::FLAG_SUPPORTS_MOVE))
+ return {};
+
+ return QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "moveDocument",
+ contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
+ sourceDocumentUri.object<QtJniTypes::UriType>(),
+ sourceParentDocumentUri.object<QtJniTypes::UriType>(),
+ targetParentDocumentUri.object<QtJniTypes::UriType>());
+}
+
+QJniObject renameDocument(const QJniObject &documentUri, const QString &displayName)
+{
+ const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
+ if (!(flags & Document::FLAG_SUPPORTS_RENAME))
+ return {};
+
+ return QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
+ "renameDocument",
+ contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
+ documentUri.object<QtJniTypes::UriType>(),
+ QJniObject::fromString(displayName).object<jstring>());
+}
+} // End DocumentsContract namespace
+
+// Start of DocumentFile
+
+using namespace DocumentsContract;
+
+namespace {
+class MakeableDocumentFile : public DocumentFile
+{
+public:
+ MakeableDocumentFile(const QJniObject &uri, const DocumentFilePtr &parent = {})
+ : DocumentFile(uri, parent)
+ {}
+};
+}
+
+DocumentFile::DocumentFile(const QJniObject &uri,
+ const DocumentFilePtr &parent)
+ : m_uri{uri}
+ , m_parent{parent}
+{}
+
+QJniObject parseUri(const QString &uri)
+{
+ QString uriToParse = uri;
+ if (uriToParse.contains(' '))
+ uriToParse.replace(' ', QUrl::toPercentEncoding(" "));
+
+ return QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::Uri>::className(),
+ "parse",
+ QJniObject::fromString(uriToParse).object<jstring>());
+}
+
+DocumentFilePtr DocumentFile::parseFromAnyUri(const QString &fileName)
+{
+ const QString encodedUri = QUrl(fileName).toEncoded();
+ const QJniObject uri = parseUri(encodedUri);
+
+ if (DocumentsContract::isDocumentUri(uri) || !DocumentsContract::isTreeUri(uri))
+ return fromSingleUri(uri);
+
+ const QString documentType = "/document/"_L1;
+ const QString treeType = "/tree/"_L1;
+
+ const int treeIndex = encodedUri.indexOf(treeType);
+ const int documentIndex = encodedUri.indexOf(documentType);
+ const int index = fileName.lastIndexOf("/");
+
+ if (index <= treeIndex + treeType.size() || index <= documentIndex + documentType.size())
+ return fromTreeUri(uri);
+
+ const QString parentUrl = encodedUri.left(index);
+ DocumentFilePtr parentDocFile = fromTreeUri(parseUri(parentUrl));
+
+ const QString baseName = encodedUri.mid(index);
+ const QString fileUrl = parentUrl + QUrl::toPercentEncoding(baseName);
+
+ DocumentFilePtr docFile = std::make_shared<MakeableDocumentFile>(parseUri(fileUrl));
+ if (parentDocFile && parentDocFile->isDirectory())
+ docFile->m_parent = parentDocFile;
+
+ return docFile;
+}
+
+DocumentFilePtr DocumentFile::fromSingleUri(const QJniObject &uri)
+{
+ return std::make_shared<MakeableDocumentFile>(uri);
+}
+
+DocumentFilePtr DocumentFile::fromTreeUri(const QJniObject &treeUri)
+{
+ QString docId;
+ if (isDocumentUri(treeUri))
+ docId = documentId(treeUri);
+ else
+ docId = treeDocumentId(treeUri);
+
+ return std::make_shared<MakeableDocumentFile>(buildDocumentUriUsingTree(treeUri, docId));
+}
+
+DocumentFilePtr DocumentFile::createFile(const QString &mimeType, const QString &displayName)
+{
+ if (isDirectory()) {
+ return std::make_shared<MakeableDocumentFile>(
+ createDocument(m_uri, mimeType, displayName),
+ shared_from_this());
+ }
+ return {};
+}
+
+DocumentFilePtr DocumentFile::createDirectory(const QString &displayName)
+{
+ if (isDirectory()) {
+ return std::make_shared<MakeableDocumentFile>(
+ createDocument(m_uri, Document::MIME_TYPE_DIR, displayName),
+ shared_from_this());
+ }
+ return {};
+}
+
+const QJniObject &DocumentFile::uri() const
+{
+ return m_uri;
+}
+
+const DocumentFilePtr &DocumentFile::parent() const
+{
+ return m_parent;
+}
+
+QString DocumentFile::name() const
+{
+ return Cursor::queryColumn(m_uri, Document::COLUMN_DISPLAY_NAME).toString();
+}
+
+QString DocumentFile::id() const
+{
+ return DocumentsContract::documentId(uri());
+}
+
+QString DocumentFile::mimeType() const
+{
+ return Cursor::queryColumn(m_uri, Document::COLUMN_MIME_TYPE).toString();
+}
+
+bool DocumentFile::isDirectory() const
+{
+ return mimeType() == Document::MIME_TYPE_DIR;
+}
+
+bool DocumentFile::isFile() const
+{
+ const QString type = mimeType();
+ return type != Document::MIME_TYPE_DIR && !type.isEmpty();
+}
+
+bool DocumentFile::isVirtual() const
+{
+ return isDocumentUri(m_uri) && (Cursor::queryColumn(m_uri,
+ Document::COLUMN_FLAGS).toInt() & Document::FLAG_VIRTUAL_DOCUMENT);
+}
+
+QDateTime DocumentFile::lastModified() const
+{
+ const auto timeVariant = Cursor::queryColumn(m_uri, Document::COLUMN_LAST_MODIFIED);
+ if (timeVariant.isValid())
+ return QDateTime::fromMSecsSinceEpoch(timeVariant.toLongLong());
+ return {};
+}
+
+int64_t DocumentFile::length() const
+{
+ return Cursor::queryColumn(m_uri, Document::COLUMN_SIZE).toLongLong();
+}
+
+namespace {
+constexpr int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
+constexpr int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
+}
+
+bool DocumentFile::canRead() const
+{
+ const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
+ const bool selfUriPermission = context.callMethod<jint>("checkCallingOrSelfUriPermission",
+ m_uri.object<QtJniTypes::UriType>(),
+ FLAG_GRANT_READ_URI_PERMISSION);
+ if (selfUriPermission != 0)
+ return false;
+
+ return !mimeType().isEmpty();
}
+
+bool DocumentFile::canWrite() const
+{
+ const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
+ const bool selfUriPermission = context.callMethod<jint>("checkCallingOrSelfUriPermission",
+ m_uri.object<QtJniTypes::UriType>(),
+ FLAG_GRANT_WRITE_URI_PERMISSION);
+ if (selfUriPermission != 0)
+ return false;
+
+ const QString type = mimeType();
+ if (type.isEmpty())
+ return false;
+
+ const int flags = Cursor::queryColumn(m_uri, Document::COLUMN_FLAGS).toInt();
+ if (flags & Document::FLAG_SUPPORTS_DELETE)
+ return true;
+
+ const bool supportsWrite = (flags & Document::FLAG_SUPPORTS_WRITE);
+ const bool isDir = (type == Document::MIME_TYPE_DIR);
+ const bool dirSupportsCreate = (isDir && (flags & Document::FLAG_DIR_SUPPORTS_CREATE));
+
+ return dirSupportsCreate || supportsWrite;
+}
+
+bool DocumentFile::remove()
+{
+ return deleteDocument(m_uri);
+}
+
+bool DocumentFile::exists() const
+{
+ return !name().isEmpty();
+}
+
+std::vector<DocumentFilePtr> DocumentFile::listFiles()
+{
+ std::vector<DocumentFilePtr> res;
+ const auto childrenUri = buildChildDocumentsUriUsingTree(m_uri, documentId(m_uri));
+ const auto query = Cursor::queryUri(childrenUri, {Document::COLUMN_DOCUMENT_ID});
+ if (!query)
+ return res;
+
+ while (query->moveToNext()) {
+ const auto uri = buildDocumentUriUsingTree(m_uri, query->data(0).toString());
+ res.push_back(std::make_shared<MakeableDocumentFile>(uri, shared_from_this()));
+ }
+ return res;
+}
+
+bool DocumentFile::rename(const QString &newName)
+{
+ QJniObject uri;
+ if (newName.startsWith("content://"_L1)) {
+ auto lastSeparatorIndex = [](const QString &file) {
+ int posDecoded = file.lastIndexOf("/");
+ int posEncoded = file.lastIndexOf(QUrl::toPercentEncoding("/"));
+ return posEncoded > posDecoded ? posEncoded : posDecoded;
+ };
+
+ // first try to see if the new file is under the same tree and thus used rename only
+ const QString parent = m_uri.toString().left(lastSeparatorIndex(m_uri.toString()));
+ if (newName.contains(parent)) {
+ QString displayName = newName.mid(lastSeparatorIndex(newName));
+ if (displayName.startsWith('/'))
+ displayName.remove(0, 1);
+ else if (displayName.startsWith(QUrl::toPercentEncoding("/")))
+ displayName.remove(0, 3);
+
+ uri = renameDocument(m_uri, displayName);
+ } else {
+ // Move
+ QJniObject srcParentUri = fromTreeUri(parseUri(parent))->uri();
+ const QString destParent = newName.left(lastSeparatorIndex(newName));
+ QJniObject targetParentUri = fromTreeUri(parseUri(destParent))->uri();
+ uri = moveDocument(m_uri, srcParentUri, targetParentUri);
+ }
+ } else {
+ uri = renameDocument(m_uri, newName);
+ }
+
+ if (uri.isValid()) {
+ m_uri = uri;
+ return true;
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
+
+// End of DocumentFile
diff --git a/src/plugins/platforms/android/androidcontentfileengine.h b/src/plugins/platforms/android/androidcontentfileengine.h
index 31eaf9b0ab..a5dd1b30f3 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.h
+++ b/src/plugins/platforms/android/androidcontentfileengine.h
@@ -1,81 +1,112 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Volker Krause <vkrause@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Volker Krause <vkrause@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDCONTENTFILEENGINE_H
#define ANDROIDCONTENTFILEENGINE_H
#include <private/qfsfileengine_p.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+using DocumentFilePtr = std::shared_ptr<class DocumentFile>;
+
class AndroidContentFileEngine : public QFSFileEngine
{
public:
AndroidContentFileEngine(const QString &fileName);
- bool open(QIODevice::OpenMode openMode) override;
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override;
+ bool close() override;
qint64 size() const override;
+ bool remove() override;
+ bool rename(const QString &newName) override;
+ bool mkdir(const QString &dirName, bool createParentDirectories,
+ std::optional<QFile::Permissions> permissions = std::nullopt) const override;
+ bool rmdir(const QString &dirName, bool recurseParentDirectories) const override;
+ QByteArray id() const override;
+ bool caseSensitive() const override { return true; }
+ QDateTime fileTime(QFile::FileTime time) const override;
FileFlags fileFlags(FileFlags type = FileInfoAll) const override;
QString fileName(FileName file = DefaultName) const override;
- QAbstractFileEngine::Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override;
- QAbstractFileEngine::Iterator *endEntryList() override;
+ IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters,
+ const QStringList &filterNames) override;
+
private:
- QString m_file;
+ void closeNativeFileDescriptor();
+ QString m_initialFile;
+ QJniObject m_pfd;
+ DocumentFilePtr m_documentFile;
};
class AndroidContentFileEngineHandler : public QAbstractFileEngineHandler
{
+ Q_DISABLE_COPY_MOVE(AndroidContentFileEngineHandler)
public:
AndroidContentFileEngineHandler();
~AndroidContentFileEngineHandler();
- QAbstractFileEngine *create(const QString &fileName) const override;
+ std::unique_ptr<QAbstractFileEngine> create(const QString &fileName) const override;
};
class AndroidContentFileEngineIterator : public QAbstractFileEngineIterator
{
public:
- AndroidContentFileEngineIterator(QDir::Filters filters, const QStringList &filterNames);
+ AndroidContentFileEngineIterator(const QString &path, QDir::Filters filters,
+ const QStringList &filterNames);
~AndroidContentFileEngineIterator();
- QString next() override;
- bool hasNext() const override;
+
+ bool advance() override;
+
QString currentFileName() const override;
+ QString currentFilePath() const override;
private:
- mutable QStringList m_entries;
- mutable int m_index = -1;
+ mutable QList<DocumentFilePtr> m_files;
+ mutable qsizetype m_index = -1;
+};
+
+/*!
+ *
+ * DocumentFile Api.
+ * Check https://developer.android.com/reference/androidx/documentfile/provider/DocumentFile
+ * for more information.
+ *
+ */
+class DocumentFile : public std::enable_shared_from_this<DocumentFile>
+{
+public:
+ static DocumentFilePtr parseFromAnyUri(const QString &filename);
+ static DocumentFilePtr fromSingleUri(const QJniObject &uri);
+ static DocumentFilePtr fromTreeUri(const QJniObject &treeUri);
+
+ DocumentFilePtr createFile(const QString &mimeType, const QString &displayName);
+ DocumentFilePtr createDirectory(const QString &displayName);
+ const QJniObject &uri() const;
+ const DocumentFilePtr &parent() const;
+ QString name() const;
+ QString id() const;
+ QString mimeType() const;
+ bool isDirectory() const;
+ bool isFile() const;
+ bool isVirtual() const;
+ QDateTime lastModified() const;
+ int64_t length() const;
+ bool canRead() const;
+ bool canWrite() const;
+ bool remove();
+ bool exists() const;
+ std::vector<DocumentFilePtr> listFiles();
+ bool rename(const QString &newName);
+
+protected:
+ DocumentFile(const QJniObject &uri, const std::shared_ptr<DocumentFile> &parent);
+
+protected:
+ QJniObject m_uri;
+ DocumentFilePtr m_parent;
};
+QT_END_NAMESPACE
+
#endif // ANDROIDCONTENTFILEENGINE_H
diff --git a/src/plugins/platforms/android/androiddeadlockprotector.cpp b/src/plugins/platforms/android/androiddeadlockprotector.cpp
deleted file mode 100644
index 2bac55f160..0000000000
--- a/src/plugins/platforms/android/androiddeadlockprotector.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "androiddeadlockprotector.h"
-
-QAtomicInt AndroidDeadlockProtector::s_blocked(0);
-
diff --git a/src/plugins/platforms/android/androiddeadlockprotector.h b/src/plugins/platforms/android/androiddeadlockprotector.h
index 7fa5bcfcb9..22b0bed523 100644
--- a/src/plugins/platforms/android/androiddeadlockprotector.h
+++ b/src/plugins/platforms/android/androiddeadlockprotector.h
@@ -1,70 +1,28 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROID_DEADLOCKPROTECTOR_H
#define ANDROID_DEADLOCKPROTECTOR_H
-#include <QAtomicInt>
+#include <QtCore/private/qjnihelpers_p.h>
QT_BEGIN_NAMESPACE
class AndroidDeadlockProtector
{
public:
- AndroidDeadlockProtector()
- : m_acquired(0)
- {
- }
-
~AndroidDeadlockProtector() {
if (m_acquired)
- s_blocked.storeRelease(0);
+ QtAndroidPrivate::releaseAndroidDeadlockProtector();
}
bool acquire() {
- m_acquired = s_blocked.testAndSetAcquire(0, 1);
+ m_acquired = QtAndroidPrivate::acquireAndroidDeadlockProtector();
return m_acquired;
}
private:
- static QAtomicInt s_blocked;
- int m_acquired;
+ bool m_acquired = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp
index 62460f5e6d..da5b63ef21 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.cpp
+++ b/src/plugins/platforms/android/androidjniaccessibility.cpp
@@ -1,42 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "androiddeadlockprotector.h"
#include "androidjniaccessibility.h"
#include "androidjnimain.h"
#include "qandroidplatformintegration.h"
@@ -51,11 +16,16 @@
#include <QtCore/QJniObject>
#include <QtGui/private/qhighdpiscaling_p.h>
+#include <QtCore/QObject>
+#include <QtCore/qpointer.h>
+#include <QtCore/qvarlengtharray.h>
+
static const char m_qtTag[] = "Qt A11Y";
-static const char m_classErrorMsg[] = "Can't find class \"%s\"";
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace QtAndroidAccessibility
{
static jmethodID m_addActionMethodID = 0;
@@ -67,16 +37,52 @@ namespace QtAndroidAccessibility
static jmethodID m_setEnabledMethodID = 0;
static jmethodID m_setFocusableMethodID = 0;
static jmethodID m_setFocusedMethodID = 0;
+ static jmethodID m_setHeadingMethodID = 0;
static jmethodID m_setScrollableMethodID = 0;
static jmethodID m_setTextSelectionMethodID = 0;
static jmethodID m_setVisibleToUserMethodID = 0;
static bool m_accessibilityActivated = false;
+ // This object is needed to schedule the execution of the code that
+ // deals with accessibility instances to the Qt main thread.
+ // Because of that almost every method here is split into two parts.
+ // The _helper part is executed in the context of m_accessibilityContext
+ // on the main thread. The other part is executed in Java thread.
+ Q_CONSTINIT static QPointer<QObject> m_accessibilityContext = {};
+
+ // This method is called from the Qt main thread, and normally a
+ // QGuiApplication instance will be used as a parent.
+ void createAccessibilityContextObject(QObject *parent)
+ {
+ if (m_accessibilityContext)
+ m_accessibilityContext->deleteLater();
+ m_accessibilityContext = new QObject(parent);
+ }
+
+ template <typename Func, typename Ret>
+ void runInObjectContext(QObject *context, Func &&func, Ret *retVal)
+ {
+ AndroidDeadlockProtector protector;
+ if (!protector.acquire()) {
+ __android_log_print(ANDROID_LOG_WARN, m_qtTag,
+ "Could not run accessibility call in object context, accessing "
+ "main thread could lead to deadlock");
+ return;
+ }
+
+ if (!QtAndroid::blockEventLoopsWhenSuspended()
+ || QGuiApplication::applicationState() != Qt::ApplicationSuspended) {
+ QMetaObject::invokeMethod(context, func, Qt::BlockingQueuedConnection, retVal);
+ } else {
+ __android_log_print(ANDROID_LOG_WARN, m_qtTag,
+ "Could not run accessibility call in object context, event loop suspended.");
+ }
+ }
+
void initialize()
{
- QJniObject::callStaticMethod<void>(QtAndroid::applicationClass(),
- "initializeAccessibility");
+ QtAndroid::qtActivityDelegate().callMethod<void>("initializeAccessibility");
}
bool isActive()
@@ -108,14 +114,23 @@ namespace QtAndroidAccessibility
return iface;
}
- void notifyLocationChange()
+ void notifyLocationChange(uint accessibilityObjectId)
{
- QtAndroid::notifyAccessibilityLocationChange();
+ QtAndroid::notifyAccessibilityLocationChange(accessibilityObjectId);
}
+ static int parentId_helper(int objectId); // forward declaration
+
void notifyObjectHide(uint accessibilityObjectId)
{
- QtAndroid::notifyObjectHide(accessibilityObjectId);
+ const auto parentObjectId = parentId_helper(accessibilityObjectId);
+ QtAndroid::notifyObjectHide(accessibilityObjectId, parentObjectId);
+ }
+
+ void notifyObjectShow(uint accessibilityObjectId)
+ {
+ const auto parentObjectId = parentId_helper(accessibilityObjectId);
+ QtAndroid::notifyObjectShow(parentObjectId);
}
void notifyObjectFocus(uint accessibilityObjectId)
@@ -123,7 +138,20 @@ namespace QtAndroidAccessibility
QtAndroid::notifyObjectFocus(accessibilityObjectId);
}
- static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ static jstring jvalueForAccessibleObject(int objectId); // forward declaration
+
+ void notifyValueChanged(uint accessibilityObjectId)
+ {
+ jstring value = jvalueForAccessibleObject(accessibilityObjectId);
+ QtAndroid::notifyValueChanged(accessibilityObjectId, value);
+ }
+
+ void notifyScrolledEvent(uint accessiblityObjectId)
+ {
+ QtAndroid::notifyScrolledEvent(accessiblityObjectId);
+ }
+
+ static QVarLengthArray<int, 8> childIdListForAccessibleObject_helper(int objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->isValid()) {
@@ -135,6 +163,18 @@ namespace QtAndroidAccessibility
if (child && child->isValid())
ifaceIdArray.append(QAccessible::uniqueId(child));
}
+ return ifaceIdArray;
+ }
+ return {};
+ }
+
+ static jintArray childIdListForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ {
+ if (m_accessibilityContext) {
+ QVarLengthArray<jint, 8> ifaceIdArray;
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return childIdListForAccessibleObject_helper(objectId);
+ }, &ifaceIdArray);
jintArray jArray = env->NewIntArray(jsize(ifaceIdArray.count()));
env->SetIntArrayRegion(jArray, 0, ifaceIdArray.count(), ifaceIdArray.data());
return jArray;
@@ -143,7 +183,7 @@ namespace QtAndroidAccessibility
return env->NewIntArray(jsize(0));
}
- static jint parentId(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static int parentId_helper(int objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->isValid()) {
@@ -157,7 +197,18 @@ namespace QtAndroidAccessibility
return -1;
}
- static jobject screenRect(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ static jint parentId(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ {
+ jint result = -1;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return parentId_helper(objectId);
+ }, &result);
+ }
+ return result;
+ }
+
+ static QRect screenRect_helper(int objectId, bool clip = true)
{
QRect rect;
QAccessibleInterface *iface = interfaceFromId(objectId);
@@ -165,18 +216,28 @@ namespace QtAndroidAccessibility
rect = QHighDpi::toNativePixels(iface->rect(), iface->window());
}
// If the widget is not fully in-bound in its parent then we have to clip the rectangle to draw
- if (iface && iface->parent() && iface->parent()->isValid()) {
+ if (clip && iface && iface->parent() && iface->parent()->isValid()) {
const auto parentRect = QHighDpi::toNativePixels(iface->parent()->rect(), iface->parent()->window());
rect = rect.intersected(parentRect);
}
+ return rect;
+ }
+ static jobject screenRect(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ {
+ QRect rect;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return screenRect_helper(objectId);
+ }, &rect);
+ }
jclass rectClass = env->FindClass("android/graphics/Rect");
jmethodID ctor = env->GetMethodID(rectClass, "<init>", "(IIII)V");
jobject jrect = env->NewObject(rectClass, ctor, rect.left(), rect.top(), rect.right(), rect.bottom());
return jrect;
}
- static jint hitTest(JNIEnv */*env*/, jobject /*thiz*/, jfloat x, jfloat y)
+ static int hitTest_helper(float x, float y)
{
QAccessibleInterface *root = interfaceFromId(-1);
if (root && root->isValid()) {
@@ -194,17 +255,29 @@ namespace QtAndroidAccessibility
return -1;
}
+ static jint hitTest(JNIEnv */*env*/, jobject /*thiz*/, jfloat x, jfloat y)
+ {
+ jint result = -1;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [x, y]() {
+ return hitTest_helper(x, y);
+ }, &result);
+ }
+ return result;
+ }
+
static void invokeActionOnInterfaceInMainThread(QAccessibleActionInterface* actionInterface,
const QString& action)
{
+ // Queue the action and return back to Java thread, so that we do not
+ // block it for too long
QMetaObject::invokeMethod(qApp, [actionInterface, action]() {
actionInterface->doAction(action);
- });
+ }, Qt::QueuedConnection);
}
- static jboolean clickAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static bool clickAction_helper(int objectId)
{
-// qDebug() << "A11Y: CLICK: " << objectId;
QAccessibleInterface *iface = interfaceFromId(objectId);
if (!iface || !iface->isValid() || !iface->actionInterface())
return false;
@@ -223,94 +296,241 @@ namespace QtAndroidAccessibility
return true;
}
- static jboolean scrollForward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static jboolean clickAction(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
{
- QAccessibleInterface *iface = interfaceFromId(objectId);
- if (iface && iface->isValid())
- return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::increaseAction());
- return false;
+ bool result = false;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return clickAction_helper(objectId);
+ }, &result);
+ }
+ return result;
}
- static jboolean scrollBackward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ static bool scroll_helper(int objectId, const QString &actionName)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
if (iface && iface->isValid())
- return QAccessibleBridgeUtils::performEffectiveAction(iface, QAccessibleActionInterface::decreaseAction());
+ return QAccessibleBridgeUtils::performEffectiveAction(iface, actionName);
return false;
}
+ static jboolean scrollForward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ {
+ bool result = false;
-#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
-clazz = env->FindClass(CLASS_NAME); \
-if (!clazz) { \
- __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
- return JNI_FALSE; \
-}
+ const auto& ids = childIdListForAccessibleObject_helper(objectId);
+ if (ids.isEmpty())
+ return false;
+
+ const int firstChildId = ids.first();
+ const QRect oldPosition = screenRect_helper(firstChildId, false);
+
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return scroll_helper(objectId, QAccessibleActionInterface::increaseAction());
+ }, &result);
+ }
+
+ // Don't check for position change if the call was not successful
+ return result && oldPosition != screenRect_helper(firstChildId, false);
+ }
+
+ static jboolean scrollBackward(JNIEnv */*env*/, jobject /*thiz*/, jint objectId)
+ {
+ bool result = false;
- //__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
+ const auto& ids = childIdListForAccessibleObject_helper(objectId);
+ if (ids.isEmpty())
+ return false;
+
+ const int firstChildId = ids.first();
+ const QRect oldPosition = screenRect_helper(firstChildId, false);
+
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return scroll_helper(objectId, QAccessibleActionInterface::decreaseAction());
+ }, &result);
+ }
+ // Don't check for position change if the call was not successful
+ return result && oldPosition != screenRect_helper(firstChildId, false);
+ }
+
+ static QString textFromValue(QAccessibleInterface *iface)
+ {
+ QString valueStr;
+ QAccessibleValueInterface *valueIface = iface->valueInterface();
+ if (valueIface) {
+ const QVariant valueVar = valueIface->currentValue();
+ const auto type = valueVar.typeId();
+ if (type == QMetaType::Double || type == QMetaType::Float) {
+ // QVariant's toString() formats floating-point values with
+ // FloatingPointShortest, which is not an accessible
+ // representation; nor, in many cases, is it suitable to the UI
+ // element whose value we're looking at. So roll our own
+ // A11Y-friendly conversion to string.
+ const double val = valueVar.toDouble();
+ // Try to use minimumStepSize() to determine precision
+ bool stepIsValid = false;
+ const double step = qAbs(valueIface->minimumStepSize().toDouble(&stepIsValid));
+ if (!stepIsValid || qFuzzyIsNull(step)) {
+ // Ignore step, use default precision
+ valueStr = qFuzzyIsNull(val) ? u"0"_s : QString::number(val, 'f');
+ } else {
+ const int precision = [](double s) {
+ int count = 0;
+ while (s < 1. && !qFuzzyCompare(s, 1.)) {
+ ++count;
+ s *= 10;
+ }
+ // If s is now 1.25, we want to show some more digits,
+ // but don't want to get silly with a step like 1./7;
+ // so only include a few extra digits.
+ const int stop = count + 3;
+ const auto fractional = [](double v) {
+ double whole = 0.0;
+ std::modf(v + 0.5, &whole);
+ return qAbs(v - whole);
+ };
+ s = fractional(s);
+ while (count < stop && !qFuzzyIsNull(s)) {
+ ++count;
+ s = fractional(s * 10);
+ }
+ return count;
+ }(step);
+ valueStr = qFuzzyIsNull(val / step) ? u"0"_s
+ : QString::number(val, 'f', precision);
+ }
+ } else {
+ valueStr = valueVar.toString();
+ }
+ }
+ return valueStr;
+ }
+ static jstring jvalueForAccessibleObject(int objectId)
+ {
+ QAccessibleInterface *iface = interfaceFromId(objectId);
+ const QString value = textFromValue(iface);
+ QJniEnvironment env;
+ jstring jstr = env->NewString((jchar*)value.constData(), (jsize)value.size());
+ if (env.checkAndClearExceptions())
+ __android_log_print(ANDROID_LOG_WARN, m_qtTag, "Failed to create jstring");
+ return jstr;
+ }
- static jstring descriptionForAccessibleObject_helper(JNIEnv *env, QAccessibleInterface *iface)
+ static QString descriptionForInterface(QAccessibleInterface *iface)
{
QString desc;
if (iface && iface->isValid()) {
+ bool hasValue = false;
desc = iface->text(QAccessible::Name);
if (desc.isEmpty())
desc = iface->text(QAccessible::Description);
if (desc.isEmpty()) {
desc = iface->text(QAccessible::Value);
- if (desc.isEmpty()) {
- if (QAccessibleValueInterface *valueIface = iface->valueInterface()) {
- desc= valueIface->currentValue().toString();
- }
+ hasValue = !desc.isEmpty();
+ }
+ if (!hasValue && iface->valueInterface()) {
+ const QString valueStr = textFromValue(iface);
+ if (!valueStr.isEmpty()) {
+ if (!desc.isEmpty())
+ desc.append(QChar(QChar::Space));
+ desc.append(valueStr);
}
}
}
- return env->NewString((jchar*) desc.constData(), (jsize) desc.size());
+ return desc;
}
- static jstring descriptionForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ static QString descriptionForAccessibleObject_helper(int objectId)
{
QAccessibleInterface *iface = interfaceFromId(objectId);
- return descriptionForAccessibleObject_helper(env, iface);
+ return descriptionForInterface(iface);
+ }
+
+ static jstring descriptionForAccessibleObject(JNIEnv *env, jobject /*thiz*/, jint objectId)
+ {
+ QString desc;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return descriptionForAccessibleObject_helper(objectId);
+ }, &desc);
+ }
+ return env->NewString((jchar*) desc.constData(), (jsize) desc.size());
}
- static bool populateNode(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject node)
+
+ struct NodeInfo
+ {
+ bool valid = false;
+ QAccessible::State state;
+ QAccessible::Role role;
+ QStringList actions;
+ QString description;
+ bool hasTextSelection = false;
+ int selectionStart = 0;
+ int selectionEnd = 0;
+ };
+
+ static NodeInfo populateNode_helper(int objectId)
{
+ NodeInfo info;
QAccessibleInterface *iface = interfaceFromId(objectId);
- if (!iface || !iface->isValid()) {
+ if (iface && iface->isValid()) {
+ info.valid = true;
+ info.state = iface->state();
+ info.role = iface->role();
+ info.actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
+ info.description = descriptionForInterface(iface);
+ QAccessibleTextInterface *textIface = iface->textInterface();
+ if (textIface && (textIface->selectionCount() > 0)) {
+ info.hasTextSelection = true;
+ textIface->selection(0, &info.selectionStart, &info.selectionEnd);
+ }
+ }
+ return info;
+ }
+
+ static jboolean populateNode(JNIEnv *env, jobject /*thiz*/, jint objectId, jobject node)
+ {
+ NodeInfo info;
+ if (m_accessibilityContext) {
+ runInObjectContext(m_accessibilityContext, [objectId]() {
+ return populateNode_helper(objectId);
+ }, &info);
+ }
+ if (!info.valid) {
__android_log_print(ANDROID_LOG_WARN, m_qtTag, "Accessibility: populateNode for Invalid ID");
return false;
}
- QAccessible::State state = iface->state();
- const QStringList actions = QAccessibleBridgeUtils::effectiveActionNames(iface);
- const bool hasClickableAction = actions.contains(QAccessibleActionInterface::pressAction())
- || actions.contains(QAccessibleActionInterface::toggleAction());
- const bool hasIncreaseAction = actions.contains(QAccessibleActionInterface::increaseAction());
- const bool hasDecreaseAction = actions.contains(QAccessibleActionInterface::decreaseAction());
- // try to fill in the text property, this is what the screen reader reads
- jstring jdesc = descriptionForAccessibleObject_helper(env, iface);
-
- if (QAccessibleTextInterface *textIface = iface->textInterface()) {
- if (m_setTextSelectionMethodID && textIface->selectionCount() > 0) {
- int startSelection;
- int endSelection;
- textIface->selection(0, &startSelection, &endSelection);
- env->CallVoidMethod(node, m_setTextSelectionMethodID, startSelection, endSelection);
- }
+ const bool hasClickableAction =
+ info.actions.contains(QAccessibleActionInterface::pressAction()) ||
+ info.actions.contains(QAccessibleActionInterface::toggleAction());
+ const bool hasIncreaseAction =
+ info.actions.contains(QAccessibleActionInterface::increaseAction());
+ const bool hasDecreaseAction =
+ info.actions.contains(QAccessibleActionInterface::decreaseAction());
+
+ if (info.hasTextSelection && m_setTextSelectionMethodID) {
+ env->CallVoidMethod(node, m_setTextSelectionMethodID, info.selectionStart,
+ info.selectionEnd);
}
- env->CallVoidMethod(node, m_setCheckableMethodID, (bool)state.checkable);
- env->CallVoidMethod(node, m_setCheckedMethodID, (bool)state.checked);
- env->CallVoidMethod(node, m_setEditableMethodID, state.editable);
- env->CallVoidMethod(node, m_setEnabledMethodID, !state.disabled);
- env->CallVoidMethod(node, m_setFocusableMethodID, (bool)state.focusable);
- env->CallVoidMethod(node, m_setFocusedMethodID, (bool)state.focused);
- env->CallVoidMethod(node, m_setVisibleToUserMethodID, !state.invisible);
+ env->CallVoidMethod(node, m_setCheckableMethodID, (bool)info.state.checkable);
+ env->CallVoidMethod(node, m_setCheckedMethodID, (bool)info.state.checked);
+ env->CallVoidMethod(node, m_setEditableMethodID, info.state.editable);
+ env->CallVoidMethod(node, m_setEnabledMethodID, !info.state.disabled);
+ env->CallVoidMethod(node, m_setFocusableMethodID, (bool)info.state.focusable);
+ env->CallVoidMethod(node, m_setFocusedMethodID, (bool)info.state.focused);
+ if (m_setHeadingMethodID)
+ env->CallVoidMethod(node, m_setHeadingMethodID, info.role == QAccessible::Heading);
+ env->CallVoidMethod(node, m_setVisibleToUserMethodID, !info.state.invisible);
env->CallVoidMethod(node, m_setScrollableMethodID, hasIncreaseAction || hasDecreaseAction);
- env->CallVoidMethod(node, m_setClickableMethodID, hasClickableAction);
+ env->CallVoidMethod(node, m_setClickableMethodID, hasClickableAction || info.role == QAccessible::Link);
// Add ACTION_CLICK
if (hasClickableAction)
@@ -324,14 +544,16 @@ if (!clazz) { \
if (hasDecreaseAction)
env->CallVoidMethod(node, m_addActionMethodID, (int)0x00002000); // ACTION_SCROLL_BACKWARD defined in AccessibilityNodeInfo
-
+ // try to fill in the text property, this is what the screen reader reads
+ jstring jdesc = env->NewString((jchar*)info.description.constData(),
+ (jsize)info.description.size());
//CALL_METHOD(node, "setText", "(Ljava/lang/CharSequence;)V", jdesc)
env->CallVoidMethod(node, m_setContentDescriptionMethodID, jdesc);
return true;
}
- static JNINativeMethod methods[] = {
+ static const JNINativeMethod methods[] = {
{"setActive","(Z)V",(void*)setActive},
{"childIdListForAccessibleObject", "(I)[I", (jintArray)childIdListForAccessibleObject},
{"parentId", "(I)I", (void*)parentId},
@@ -351,13 +573,10 @@ if (!clazz) { \
return false; \
}
- bool registerNatives(JNIEnv *env)
+ bool registerNatives(QJniEnvironment &env)
{
- jclass clazz;
- FIND_AND_CHECK_CLASS("org/qtproject/qt/android/accessibility/QtNativeAccessibility");
- jclass appClass = static_cast<jclass>(env->NewGlobalRef(clazz));
-
- if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+ if (!env.registerNativeMethods("org/qtproject/qt/android/QtNativeAccessibility",
+ methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt A11y", "RegisterNatives failed");
return false;
}
@@ -372,6 +591,9 @@ if (!clazz) { \
GET_AND_CHECK_STATIC_METHOD(m_setEnabledMethodID, nodeInfoClass, "setEnabled", "(Z)V");
GET_AND_CHECK_STATIC_METHOD(m_setFocusableMethodID, nodeInfoClass, "setFocusable", "(Z)V");
GET_AND_CHECK_STATIC_METHOD(m_setFocusedMethodID, nodeInfoClass, "setFocused", "(Z)V");
+ if (QtAndroidPrivate::androidSdkVersion() >= 28) {
+ GET_AND_CHECK_STATIC_METHOD(m_setHeadingMethodID, nodeInfoClass, "setHeading", "(Z)V");
+ }
GET_AND_CHECK_STATIC_METHOD(m_setScrollableMethodID, nodeInfoClass, "setScrollable", "(Z)V");
GET_AND_CHECK_STATIC_METHOD(m_setVisibleToUserMethodID, nodeInfoClass, "setVisibleToUser", "(Z)V");
GET_AND_CHECK_STATIC_METHOD(m_setTextSelectionMethodID, nodeInfoClass, "setTextSelection", "(II)V");
diff --git a/src/plugins/platforms/android/androidjniaccessibility.h b/src/plugins/platforms/android/androidjniaccessibility.h
index de9d32a099..6e8e059334 100644
--- a/src/plugins/platforms/android/androidjniaccessibility.h
+++ b/src/plugins/platforms/android/androidjniaccessibility.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDJNIACCESSIBILITY_H
#define ANDROIDJNIACCESSIBILITY_H
@@ -44,14 +8,21 @@
QT_BEGIN_NAMESPACE
+class QObject;
+class QJniEnvironment;
+
namespace QtAndroidAccessibility
{
void initialize();
bool isActive();
- bool registerNatives(JNIEnv *env);
- void notifyLocationChange();
+ bool registerNatives(QJniEnvironment &env);
+ void notifyLocationChange(uint accessibilityObjectId);
void notifyObjectHide(uint accessibilityObjectId);
+ void notifyObjectShow(uint accessibilityObjectId);
void notifyObjectFocus(uint accessibilityObjectId);
+ void notifyValueChanged(uint accessibilityObjectId);
+ void notifyScrolledEvent(uint accessibilityObjectId);
+ void createAccessibilityContextObject(QObject *parent);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjniclipboard.cpp b/src/plugins/platforms/android/androidjniclipboard.cpp
deleted file mode 100644
index 6178edd675..0000000000
--- a/src/plugins/platforms/android/androidjniclipboard.cpp
+++ /dev/null
@@ -1,134 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "androidjniclipboard.h"
-#include <QtCore/QUrl>
-#include <QtCore/QJniObject>
-#include <QtCore/QJniEnvironment>
-
-QT_BEGIN_NAMESPACE
-
-using namespace QtAndroid;
-namespace QtAndroidClipboard
-{
- QAndroidPlatformClipboard *m_manager = nullptr;
-
- static JNINativeMethod methods[] = {
- {"onClipboardDataChanged", "()V", (void *)onClipboardDataChanged}
- };
-
- void setClipboardManager(QAndroidPlatformClipboard *manager)
- {
- m_manager = manager;
- QJniObject::callStaticMethod<void>(applicationClass(), "registerClipboardManager");
- jclass appClass = QtAndroid::applicationClass();
- QJniEnvironment env;
- if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
- __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
- return;
- }
- }
- void clearClipboardData()
- {
- QJniObject::callStaticMethod<void>(applicationClass(), "clearClipData");
- }
- void setClipboardMimeData(QMimeData *data)
- {
- clearClipboardData();
- if (data->hasText()) {
- QJniObject::callStaticMethod<void>(applicationClass(),
- "setClipboardText", "(Ljava/lang/String;)V",
- QJniObject::fromString(data->text()).object());
- }
- if (data->hasHtml()) {
- QJniObject::callStaticMethod<void>(applicationClass(),
- "setClipboardHtml",
- "(Ljava/lang/String;Ljava/lang/String;)V",
- QJniObject::fromString(data->text()).object(),
- QJniObject::fromString(data->html()).object());
- }
- if (data->hasUrls()) {
- QList<QUrl> urls = data->urls();
- for (const auto &u : qAsConst(urls)) {
- QJniObject::callStaticMethod<void>(applicationClass(),
- "setClipboardUri",
- "(Ljava/lang/String;)V",
- QJniObject::fromString(u.toEncoded()).object());
- }
- }
- }
-
- QMimeData *getClipboardMimeData()
- {
- QMimeData *data = new QMimeData;
- if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardText")) {
- data->setText(QJniObject::callStaticObjectMethod(applicationClass(),
- "getClipboardText",
- "()Ljava/lang/String;").toString());
- }
- if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardHtml")) {
- data->setHtml(QJniObject::callStaticObjectMethod(applicationClass(),
- "getClipboardHtml",
- "()Ljava/lang/String;").toString());
- }
- if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardUri")) {
- QJniObject uris = QJniObject::callStaticObjectMethod(applicationClass(),
- "getClipboardUris",
- "()[Ljava/lang/String;");
- if (uris.isValid()) {
- QList<QUrl> urls;
- QJniEnvironment env;
- jobjectArray juris = uris.object<jobjectArray>();
- const jint nUris = env->GetArrayLength(juris);
- urls.reserve(static_cast<int>(nUris));
- for (int i = 0; i < nUris; ++i)
- urls << QUrl(QJniObject(env->GetObjectArrayElement(juris, i)).toString());
- data->setUrls(urls);
- }
- }
- return data;
- }
-
- void onClipboardDataChanged(JNIEnv */*env*/, jobject /*thiz*/)
- {
- m_manager->emitChanged(QClipboard::Clipboard);
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjniclipboard.h b/src/plugins/platforms/android/androidjniclipboard.h
deleted file mode 100644
index 96992340a6..0000000000
--- a/src/plugins/platforms/android/androidjniclipboard.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef ANDROIDJNICLIPBOARD_H
-#define ANDROIDJNICLIPBOARD_H
-
-#include <QString>
-#include "qandroidplatformclipboard.h"
-#include "androidjnimain.h"
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidPlatformClipboard;
-namespace QtAndroidClipboard
-{
- // Clipboard support
- void setClipboardManager(QAndroidPlatformClipboard *manager);
- void setClipboardMimeData(QMimeData *data);
- QMimeData *getClipboardMimeData();
- void clearClipboardData();
- void onClipboardDataChanged(JNIEnv */*env*/, jobject /*thiz*/);
- // Clipboard support
-}
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDJNICLIPBOARD_H
diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp
index ebdc18cc07..d074e73b9e 100644
--- a/src/plugins/platforms/android/androidjniinput.cpp
+++ b/src/plugins/platforms/android/androidjniinput.cpp
@@ -1,42 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: http://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtGui/qtguiglobal.h>
@@ -44,6 +9,7 @@
#include "androidjnimain.h"
#include "qandroidplatformintegration.h"
+#include <qpa/qplatformwindow.h>
#include <qpa/qwindowsysteminterface.h>
#include <QTouchEvent>
#include <QPointer>
@@ -53,67 +19,127 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
+
using namespace QtAndroid;
+Q_DECLARE_JNI_CLASS(QtLayout, "org/qtproject/qt/android/QtLayout")
+
namespace QtAndroidInput
{
static bool m_ignoreMouseEvents = false;
+ static Qt::MouseButtons m_buttons = Qt::NoButton;
+
static QRect m_softwareKeyboardRect;
static QList<QWindowSystemInterface::TouchPoint> m_touchPoints;
static QPointer<QWindow> m_mouseGrabber;
+ GenericMotionEventListener::~GenericMotionEventListener() {}
+ namespace {
+ struct GenericMotionEventListeners {
+ QMutex mutex;
+ QList<QtAndroidInput::GenericMotionEventListener *> listeners;
+ };
+ }
+ Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
+
+ static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
+ {
+ jboolean ret = JNI_FALSE;
+ QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
+ for (auto *listener : std::as_const(g_genericMotionEventListeners()->listeners))
+ ret |= listener->handleGenericMotionEvent(event);
+ return ret;
+ }
+
+ KeyEventListener::~KeyEventListener() {}
+ namespace {
+ struct KeyEventListeners {
+ QMutex mutex;
+ QList<QtAndroidInput::KeyEventListener *> listeners;
+ };
+ }
+ Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
+
+ static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
+ {
+ jboolean ret = JNI_FALSE;
+ QMutexLocker locker(&g_keyEventListeners()->mutex);
+ for (auto *listener : std::as_const(g_keyEventListeners()->listeners))
+ ret |= listener->handleKeyEvent(event);
+ return ret;
+ }
+
+ void registerGenericMotionEventListener(QtAndroidInput::GenericMotionEventListener *listener)
+ {
+ QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
+ g_genericMotionEventListeners()->listeners.push_back(listener);
+ }
+
+ void unregisterGenericMotionEventListener(QtAndroidInput::GenericMotionEventListener *listener)
+ {
+ QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
+ g_genericMotionEventListeners()->listeners.removeOne(listener);
+ }
+
+ void registerKeyEventListener(QtAndroidInput::KeyEventListener *listener)
+ {
+ QMutexLocker locker(&g_keyEventListeners()->mutex);
+ g_keyEventListeners()->listeners.push_back(listener);
+ }
+
+ void unregisterKeyEventListener(QtAndroidInput::KeyEventListener *listener)
+ {
+ QMutexLocker locker(&g_keyEventListeners()->mutex);
+ g_keyEventListeners()->listeners.removeOne(listener);
+ }
+
+ QJniObject qtLayout()
+ {
+ return qtActivityDelegate().callMethod<QtJniTypes::QtLayout>("getQtLayout");
+ }
+
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd)
{
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd;
-#endif
- QJniObject::callStaticMethod<void>(applicationClass(),
- "updateSelection",
- "(IIII)V",
+ qCDebug(lcQpaInputMethods) << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd;
+ qtInputDelegate().callMethod<void>("updateSelection",
selStart,
selEnd,
candidatesStart,
candidatesEnd);
}
- void showSoftwareKeyboard(int left, int top, int width, int height, int editorHeight, int inputHints, int enterKeyType)
+ void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
{
- QJniObject::callStaticMethod<void>(applicationClass(),
- "showSoftwareKeyboard",
- "(IIIIIII)V",
+ qtInputDelegate().callMethod<void>("showSoftwareKeyboard",
+ QtAndroidPrivate::activity(),
+ qtLayout().object<QtJniTypes::QtLayout>(),
left,
top,
width,
height,
- editorHeight,
inputHints,
enterKeyType);
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints << enterKeyType;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints << enterKeyType;
}
void resetSoftwareKeyboard()
{
- QJniObject::callStaticMethod<void>(applicationClass(), "resetSoftwareKeyboard");
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ RESETSOFTWAREKEYBOARD");
-#endif
+ qtInputDelegate().callMethod<void>("resetSoftwareKeyboard");
+ qCDebug(lcQpaInputMethods) << "@@@ RESETSOFTWAREKEYBOARD";
}
void hideSoftwareKeyboard()
{
- QJniObject::callStaticMethod<void>(applicationClass(), "hideSoftwareKeyboard");
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ HIDESOFTWAREKEYBOARD");
-#endif
+ qtInputDelegate().callMethod<void>("hideSoftwareKeyboard");
+ qCDebug(lcQpaInputMethods) << "@@@ HIDESOFTWAREKEYBOARD";
}
bool isSoftwareKeyboardVisible()
{
- return QJniObject::callStaticMethod<jboolean>(applicationClass(), "isSoftwareKeyboardVisible");
+ return qtInputDelegate().callMethod<jboolean>("isSoftwareKeyboardVisible");
}
QRect softwareKeyboardRect()
@@ -123,92 +149,150 @@ namespace QtAndroidInput
int getSelectHandleWidth()
{
- return QJniObject::callStaticMethod<jint>(applicationClass(), "getSelectHandleWidth");
+ return qtInputDelegate().callMethod<jint>("getSelectHandleWidth");
}
void updateHandles(int mode, QPoint editMenuPos, uint32_t editButtons, QPoint cursor, QPoint anchor, bool rtl)
{
- QJniObject::callStaticMethod<void>(applicationClass(), "updateHandles", "(IIIIIIIIZ)V",
+ qtInputDelegate().callMethod<void>("updateHandles",
+ QtAndroidPrivate::activity(),
+ qtLayout().object<QtJniTypes::QtLayout>(),
mode, editMenuPos.x(), editMenuPos.y(), editButtons,
cursor.x(), cursor.y(),
anchor.x(), anchor.y(), rtl);
}
- void updateInputItemRectangle(int left, int top, int width, int height)
+ // from https://developer.android.com/reference/android/view/MotionEvent#getButtonState()
+ enum AndroidMouseButton {
+ BUTTON_PRIMARY = 0x00000001,
+ BUTTON_SECONDARY = 0x00000002,
+ BUTTON_TERTIARY = 0x00000004,
+ BUTTON_BACK = 0x00000008,
+ BUTTON_FORWARD = 0x00000010,
+ BUTTON_STYLUS_PRIMARY = 0x00000020,
+ BUTTON_STYLUS_SECONDARY = 0x00000040,
+ };
+ Q_DECLARE_FLAGS(AndroidMouseButtons, AndroidMouseButton)
+
+ static Qt::MouseButtons toMouseButtons(jint j_buttons)
+ {
+ const auto buttons = static_cast<AndroidMouseButtons>(j_buttons);
+ Qt::MouseButtons mouseButtons;
+ if (buttons.testFlag(BUTTON_PRIMARY))
+ mouseButtons.setFlag(Qt::LeftButton);
+
+ if (buttons.testFlag(BUTTON_SECONDARY))
+ mouseButtons.setFlag(Qt::RightButton);
+
+ if (buttons.testFlag(BUTTON_TERTIARY))
+ mouseButtons.setFlag(Qt::MiddleButton);
+
+ if (buttons.testFlag(BUTTON_BACK))
+ mouseButtons.setFlag(Qt::BackButton);
+
+ if (buttons.testFlag(BUTTON_FORWARD))
+ mouseButtons.setFlag(Qt::ForwardButton);
+
+ if (buttons.testFlag(BUTTON_STYLUS_PRIMARY))
+ mouseButtons.setFlag(Qt::LeftButton);
+
+ if (buttons.testFlag(BUTTON_STYLUS_SECONDARY))
+ mouseButtons.setFlag(Qt::RightButton);
+
+ // Fall back to left button
+ if (Q_UNLIKELY(buttons != 0 && mouseButtons == Qt::NoButton)) {
+ qWarning() << "Unhandled button value:" << buttons << "Falling back to Qt::LeftButton";
+ mouseButtons = Qt::LeftButton;
+ }
+ return mouseButtons;
+ }
+
+ static void sendMouseButtonEvents(QWindow *topLevel, QPoint localPos, QPoint globalPos,
+ jint mouseButtonState, QEvent::Type type)
{
- QJniObject::callStaticMethod<void>(applicationClass(),
- "updateInputItemRectangle",
- "(IIII)V",
- left,
- top,
- width,
- height);
+ const Qt::MouseButtons mouseButtons = toMouseButtons(mouseButtonState);
+ const Qt::MouseButtons changedButtons = mouseButtons & ~m_buttons;
+
+ if (changedButtons == Qt::NoButton)
+ return;
+
+ static_assert (sizeof(changedButtons) <= sizeof(uint), "Qt::MouseButtons size changed. Adapt code.");
+
+ for (uint buttonInt = 0x1; static_cast<uint>(changedButtons) >= buttonInt; buttonInt <<= 1) {
+ const auto button = static_cast<Qt::MouseButton>(buttonInt);
+ if (changedButtons.testFlag(button)) {
+ QWindowSystemInterface::handleMouseEvent(topLevel, localPos, globalPos,
+ mouseButtons, button, type);
+ }
+ }
}
- static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
+ static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState)
{
if (m_ignoreMouseEvents)
return;
- QPoint globalPos(x,y);
- QWindow *tlw = topLevelWindowAt(globalPos);
- m_mouseGrabber = tlw;
- QPoint localPos = tlw ? (globalPos - tlw->position()) : globalPos;
- QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos,
- Qt::MouseButtons(Qt::LeftButton),
- Qt::LeftButton, QEvent::MouseButtonPress);
- }
-
- static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
- {
- QPoint globalPos(x,y);
- QWindow *tlw = m_mouseGrabber.data();
- if (!tlw)
- tlw = topLevelWindowAt(globalPos);
- QPoint localPos = tlw ? (globalPos -tlw->position()) : globalPos;
- QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos,
- Qt::MouseButtons(Qt::NoButton),
- Qt::LeftButton, QEvent::MouseButtonRelease);
+ const QPoint globalPos(x,y);
+ QWindow *window = windowFromId(winId);
+ m_mouseGrabber = window;
+ const QPoint localPos = window && window->handle() ?
+ window->handle()->mapFromGlobal(globalPos) : globalPos;
+ sendMouseButtonEvents(window, localPos, globalPos, mouseButtonState, QEvent::MouseButtonPress);
+ }
+
+ static void mouseUp(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jint mouseButtonState)
+ {
+ const QPoint globalPos(x,y);
+ QWindow *window = m_mouseGrabber.data();
+ if (!window)
+ window = windowFromId(winId);
+
+ const QPoint localPos = window && window->handle() ?
+ window->handle()->mapFromGlobal(globalPos) : globalPos;
+
+ sendMouseButtonEvents(window, localPos, globalPos, mouseButtonState, QEvent::MouseButtonRelease);
m_ignoreMouseEvents = false;
- m_mouseGrabber = 0;
+ m_mouseGrabber.clear();
}
- static void mouseMove(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
+ static void mouseMove(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y)
{
if (m_ignoreMouseEvents)
return;
- QPoint globalPos(x,y);
- QWindow *tlw = m_mouseGrabber.data();
- if (!tlw)
- tlw = topLevelWindowAt(globalPos);
- QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos;
- QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos,
+ const QPoint globalPos(x,y);
+ QWindow *window = m_mouseGrabber.data();
+ if (!window)
+ window = windowFromId(winId);
+ const QPoint localPos = window && window->handle() ?
+ window->handle()->mapFromGlobal(globalPos) : globalPos;
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
Qt::MouseButtons(m_mouseGrabber ? Qt::LeftButton : Qt::NoButton),
Qt::NoButton, QEvent::MouseMove);
}
- static void mouseWheel(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y, jfloat hdelta, jfloat vdelta)
+ static void mouseWheel(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y, jfloat hdelta, jfloat vdelta)
{
if (m_ignoreMouseEvents)
return;
- QPoint globalPos(x,y);
- QWindow *tlw = m_mouseGrabber.data();
- if (!tlw)
- tlw = topLevelWindowAt(globalPos);
- QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos;
- QPoint angleDelta(hdelta * 120, vdelta * 120);
+ const QPoint globalPos(x,y);
+ QWindow *window = m_mouseGrabber.data();
+ if (!window)
+ window = windowFromId(winId);
+ const QPoint localPos = window && window->handle() ?
+ window->handle()->mapFromGlobal(globalPos) : globalPos;
+ const QPoint angleDelta(hdelta * 120, vdelta * 120);
- QWindowSystemInterface::handleWheelEvent(tlw,
+ QWindowSystemInterface::handleWheelEvent(window,
localPos,
globalPos,
QPoint(),
angleDelta);
}
- static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
+ static void longPress(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint x, jint y)
{
QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext();
if (inputContext && qGuiApp)
@@ -219,16 +303,17 @@ namespace QtAndroidInput
if (!rightMouseFromLongPress)
return;
m_ignoreMouseEvents = true;
- QPoint globalPos(x,y);
- QWindow *tlw = topLevelWindowAt(globalPos);
- QPoint localPos = tlw ? (globalPos-tlw->position()) : globalPos;
+ const QPoint globalPos(x,y);
+ QWindow *window = windowFromId(winId);
+ const QPoint localPos = window && window->handle() ?
+ window->handle()->mapFromGlobal(globalPos) : globalPos;
// Click right button if no other button is already pressed.
if (!m_mouseGrabber) {
- QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos,
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
Qt::MouseButtons(Qt::RightButton), Qt::RightButton,
QEvent::MouseButtonPress);
- QWindowSystemInterface::handleMouseEvent(tlw, localPos, globalPos,
+ QWindowSystemInterface::handleMouseEvent(window, localPos, globalPos,
Qt::MouseButtons(Qt::NoButton), Qt::RightButton,
QEvent::MouseButtonRelease);
}
@@ -266,12 +351,11 @@ namespace QtAndroidInput
touchPoint.rotation = qRadiansToDegrees(rotation);
touchPoint.normalPosition = QPointF(double(x / dw), double(y / dh));
touchPoint.state = state;
- touchPoint.area = QRectF(x - double(minor),
- y - double(major),
- double(minor * 2),
- double(major * 2));
+ touchPoint.area = QRectF(x - double(minor * 0.5f),
+ y - double(major * 0.5f),
+ double(minor),
+ double(major));
m_touchPoints.push_back(touchPoint);
-
if (state == QEventPoint::State::Pressed) {
QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext();
if (inputContext && qGuiApp)
@@ -279,15 +363,11 @@ namespace QtAndroidInput
}
}
- static void touchEnd(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ static QPointingDevice *getTouchDevice()
{
- if (m_touchPoints.isEmpty())
- return;
-
- QMutexLocker lock(QtAndroid::platformInterfaceMutex());
QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration();
if (!platformIntegration)
- return;
+ return nullptr;
QPointingDevice *touchDevice = platformIntegration->touchDevice();
if (!touchDevice) {
@@ -303,10 +383,41 @@ namespace QtAndroidInput
platformIntegration->setTouchDevice(touchDevice);
}
- QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
+ return touchDevice;
+ }
+
+ static void touchEnd(JNIEnv * /*env*/, jobject /*thiz*/, jint winId, jint /*action*/)
+ {
+ if (m_touchPoints.isEmpty())
+ return;
+
+ QMutexLocker lock(QtAndroid::platformInterfaceMutex());
+ const QPointingDevice *touchDevice = getTouchDevice();
+ if (!touchDevice)
+ return;
+
+ QWindow *window = QtAndroid::windowFromId(winId);
+ if (!window)
+ return;
QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints);
}
+ static void touchCancel(JNIEnv * /*env*/, jobject /*thiz*/, jint winId)
+ {
+ if (m_touchPoints.isEmpty())
+ return;
+
+ QMutexLocker lock(QtAndroid::platformInterfaceMutex());
+ const QPointingDevice *touchDevice = getTouchDevice();
+ if (!touchDevice)
+ return;
+
+ QWindow *window = QtAndroid::windowFromId(winId);
+ if (!window)
+ return;
+ QWindowSystemInterface::handleTouchCancelEvent(window, touchDevice);
+ }
+
static bool isTabletEventSupported(JNIEnv */*env*/, jobject /*thiz*/)
{
#if QT_CONFIG(tabletevent)
@@ -316,14 +427,14 @@ namespace QtAndroidInput
#endif // QT_CONFIG(tabletevent)
}
- static void tabletEvent(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint deviceId, jlong time, jint action,
+ static void tabletEvent(JNIEnv */*env*/, jobject /*thiz*/, jint winId, jint deviceId, jlong time, jint action,
jint pointerType, jint buttonState, jfloat x, jfloat y, jfloat pressure)
{
#if QT_CONFIG(tabletevent)
- QPointF globalPosF(x, y);
- QPoint globalPos((int)x, (int)y);
- QWindow *tlw = topLevelWindowAt(globalPos);
- QPointF localPos = tlw ? (globalPosF - tlw->position()) : globalPosF;
+ const QPointF globalPosF(x, y);
+ QWindow *window = windowFromId(winId);
+ const QPointF localPos = window && window->handle() ?
+ window->handle()->mapFromGlobalF(globalPosF) : globalPosF;
// Galaxy Note with plain Android:
// 0 1 0 stylus press
@@ -343,6 +454,7 @@ namespace QtAndroidInput
Qt::MouseButtons buttons = Qt::NoButton;
switch (action) {
case 1: // ACTION_UP
+ case 6: // ACTION_POINTER_UP, happens if stylus is not the primary pointer
case 212: // stylus release while side-button held on Galaxy Note 4
buttons = Qt::NoButton;
break;
@@ -354,11 +466,9 @@ namespace QtAndroidInput
break;
}
-#ifdef QT_DEBUG_ANDROID_STYLUS
- qDebug() << action << pointerType << buttonState << '@' << x << y << "pressure" << pressure << ": buttons" << buttons;
-#endif
+ qCDebug(lcQpaInputMethods) << action << pointerType << buttonState << '@' << x << y << "pressure" << pressure << ": buttons" << buttons;
- QWindowSystemInterface::handleTabletEvent(tlw, ulong(time),
+ QWindowSystemInterface::handleTabletEvent(window, ulong(time),
localPos, globalPosF, int(QInputDevice::DeviceType::Stylus), pointerType,
buttons, pressure, 0, 0, 0., 0., 0, deviceId, Qt::NoModifier);
#endif // QT_CONFIG(tabletevent)
@@ -822,9 +932,7 @@ namespace QtAndroidInput
QMetaObject::invokeMethod(inputContext, "hideSelectionHandles", Qt::QueuedConnection);
}
}
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ KEYBOARDVISIBILITYCHANGED" << inputContext;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ KEYBOARDVISIBILITYCHANGED" << inputContext;
}
static void keyboardGeometryChanged(JNIEnv */*env*/, jobject /*thiz*/, jint x, jint y, jint w, jint h)
@@ -837,16 +945,12 @@ namespace QtAndroidInput
if (inputContext && qGuiApp)
inputContext->emitKeyboardRectChanged();
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ KEYBOARDRECTCHANGED" << m_softwareKeyboardRect;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ KEYBOARDRECTCHANGED" << m_softwareKeyboardRect;
}
static void handleLocationChanged(JNIEnv */*env*/, jobject /*thiz*/, int id, int x, int y)
{
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ handleLocationChanged" << id << x << y;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ handleLocationChanged" << id << x << y;
QAndroidInputContext *inputContext = QAndroidInputContext::androidInputContext();
if (inputContext && qGuiApp)
QMetaObject::invokeMethod(inputContext, "handleLocationChanged", Qt::BlockingQueuedConnection,
@@ -854,12 +958,14 @@ namespace QtAndroidInput
}
- static JNINativeMethod methods[] = {
+
+ static const JNINativeMethod methods[] = {
{"touchBegin","(I)V",(void*)touchBegin},
{"touchAdd","(IIIZIIFFFF)V",(void*)touchAdd},
{"touchEnd","(II)V",(void*)touchEnd},
- {"mouseDown", "(III)V", (void *)mouseDown},
- {"mouseUp", "(III)V", (void *)mouseUp},
+ {"touchCancel", "(I)V", (void *)touchCancel},
+ {"mouseDown", "(IIII)V", (void *)mouseDown},
+ {"mouseUp", "(IIII)V", (void *)mouseUp},
{"mouseMove", "(III)V", (void *)mouseMove},
{"mouseWheel", "(IIIFF)V", (void *)mouseWheel},
{"longPress", "(III)V", (void *)longPress},
@@ -869,14 +975,15 @@ namespace QtAndroidInput
{"keyUp", "(IIIZ)V", (void *)keyUp},
{"keyboardVisibilityChanged", "(Z)V", (void *)keyboardVisibilityChanged},
{"keyboardGeometryChanged", "(IIII)V", (void *)keyboardGeometryChanged},
- {"handleLocationChanged", "(III)V", (void *)handleLocationChanged}
+ {"handleLocationChanged", "(III)V", (void *)handleLocationChanged},
+ {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)},
+ {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
};
- bool registerNatives(JNIEnv *env)
+ bool registerNatives(QJniEnvironment &env)
{
- jclass appClass = QtAndroid::applicationClass();
-
- if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+ if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtInputDelegate>::className(),
+ methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
return false;
}
diff --git a/src/plugins/platforms/android/androidjniinput.h b/src/plugins/platforms/android/androidjniinput.h
index ceef2a826c..28a2665bf6 100644
--- a/src/plugins/platforms/android/androidjniinput.h
+++ b/src/plugins/platforms/android/androidjniinput.h
@@ -1,55 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDJNIINPUT_H
#define ANDROIDJNIINPUT_H
#include <jni.h>
#include <QtCore/qglobal.h>
+#include <QtCore/QLoggingCategory>
#include <QtCore/QRect>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaInputMethods);
+
+class QJniEnvironment;
+
namespace QtAndroidInput
{
// Software keyboard support
- void showSoftwareKeyboard(int top, int left, int width, int editorHeight, int height, int inputHints, int enterKeyType);
+ void showSoftwareKeyboard(int top, int left, int width, int height, int inputHints, int enterKeyType);
void resetSoftwareKeyboard();
void hideSoftwareKeyboard();
bool isSoftwareKeyboardVisible();
@@ -57,14 +26,32 @@ namespace QtAndroidInput
void updateSelection(int selStart, int selEnd, int candidatesStart, int candidatesEnd);
// Software keyboard support
- // edit field resize
- void updateInputItemRectangle(int left, int top, int width, int height);
// cursor/selection handles
void updateHandles(int handleCount, QPoint editMenuPos = QPoint(), uint32_t editButtons = 0,
QPoint cursor = QPoint(), QPoint anchor = QPoint(), bool rtl = false);
int getSelectHandleWidth();
- bool registerNatives(JNIEnv *env);
+ class GenericMotionEventListener
+ {
+ public:
+ virtual ~GenericMotionEventListener();
+ virtual bool handleGenericMotionEvent(jobject event) = 0;
+ };
+
+ class KeyEventListener
+ {
+ public:
+ virtual ~KeyEventListener();
+ virtual bool handleKeyEvent(jobject event) = 0;
+ };
+
+ void registerGenericMotionEventListener(GenericMotionEventListener *listener);
+ void unregisterGenericMotionEventListener(GenericMotionEventListener *listener);
+
+ void registerKeyEventListener(KeyEventListener *listener);
+ void unregisterKeyEventListener(KeyEventListener *listener);
+
+ bool registerNatives(QJniEnvironment &env);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 6dff19f3d5..9fdcf3936b 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <dlfcn.h>
#include <pthread.h>
@@ -46,29 +10,37 @@
#include "androidcontentfileengine.h"
#include "androiddeadlockprotector.h"
#include "androidjniaccessibility.h"
-#include "androidjniclipboard.h"
#include "androidjniinput.h"
#include "androidjnimain.h"
#include "androidjnimenu.h"
+#include "androidwindowembedding.h"
#include "qandroidassetsfileenginehandler.h"
#include "qandroideventdispatcher.h"
#include "qandroidplatformdialoghelpers.h"
#include "qandroidplatformintegration.h"
+#include "qandroidplatformclipboard.h"
+#include "qandroidplatformwindow.h"
#include <android/api-level.h>
#include <android/asset_manager_jni.h>
#include <android/bitmap.h>
#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qbasicatomic.h>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjniobject.h>
+#include <QtCore/qprocess.h>
#include <QtCore/qresource.h>
+#include <QtCore/qscopeguard.h>
#include <QtCore/qthread.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qhighdpiscaling_p.h>
#include <qpa/qwindowsysteminterface.h>
+
+using namespace Qt::StringLiterals;
+
QT_BEGIN_NAMESPACE
static JavaVM *m_javaVM = nullptr;
@@ -78,11 +50,12 @@ static jmethodID m_loadClassMethodID = nullptr;
static AAssetManager *m_assetManager = nullptr;
static jobject m_assets = nullptr;
static jobject m_resourcesObj = nullptr;
-static jobject m_activityObject = nullptr;
-static jmethodID m_createSurfaceMethodID = nullptr;
-static jobject m_serviceObject = nullptr;
-static jmethodID m_setSurfaceGeometryMethodID = nullptr;
-static jmethodID m_destroySurfaceMethodID = nullptr;
+
+static jclass m_qtActivityClass = nullptr;
+static jclass m_qtServiceClass = nullptr;
+
+static QtJniTypes::QtActivityDelegateBase m_activityDelegate = nullptr;
+static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
static int m_pendingApplicationState = -1;
static QBasicMutex m_platformMutex;
@@ -101,11 +74,6 @@ static void *m_mainLibraryHnd = nullptr;
static QList<QByteArray> m_applicationParams;
static sem_t m_exitSemaphore, m_terminateSemaphore;
-QHash<int, AndroidSurfaceClient *> m_surfaces;
-
-static QBasicMutex m_surfacesMutex;
-static int m_surfaceId = 1;
-
static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
@@ -123,6 +91,10 @@ static const char m_qtTag[] = "Qt";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
+Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+Q_DECLARE_JNI_CLASS(QtEmbeddedDelegateFactory, "org/qtproject/qt/android/QtEmbeddedDelegateFactory")
+
namespace QtAndroid
{
QBasicMutex *platformInterfaceMutex()
@@ -133,6 +105,7 @@ namespace QtAndroid
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
{
m_androidPlatformIntegration = androidPlatformIntegration;
+ QtAndroid::notifyNativePluginIntegrationReady((bool)m_androidPlatformIntegration);
// flush the pending state if necessary.
if (m_androidPlatformIntegration && (m_pendingApplicationState != -1)) {
@@ -158,6 +131,21 @@ namespace QtAndroid
: 0;
}
+ QWindow *windowFromId(int windowId)
+ {
+ if (!qGuiApp)
+ return nullptr;
+
+ for (QWindow *w : qGuiApp->allWindows()) {
+ if (!w->handle())
+ continue;
+ QAndroidPlatformWindow *window = static_cast<QAndroidPlatformWindow *>(w->handle());
+ if (window->nativeViewId() == windowId)
+ return w;
+ }
+ return nullptr;
+ }
+
int availableWidthPixels()
{
return m_availableWidthPixels;
@@ -193,39 +181,96 @@ namespace QtAndroid
return m_applicationClass;
}
- jobject activity()
+ // TODO move calls from here to where they logically belong
+ void setSystemUiVisibility(SystemUiVisibility uiVisibility)
{
- return m_activityObject;
+ qtActivityDelegate().callMethod<void>("setSystemUiVisibility", jint(uiVisibility));
}
- jobject service()
+ // FIXME: avoid direct access to QtActivityDelegate
+ QtJniTypes::QtActivityDelegateBase qtActivityDelegate()
{
- return m_serviceObject;
+ using namespace QtJniTypes;
+ if (!m_activityDelegate.isValid()) {
+ if (isQtApplication()) {
+ auto context = QtAndroidPrivate::activity();
+ m_activityDelegate = context.callMethod<QtActivityDelegateBase>("getActivityDelegate");
+ } else {
+ m_activityDelegate = QJniObject::callStaticMethod<QtActivityDelegateBase>(
+ Traits<QtEmbeddedDelegateFactory>::className(),
+ "getActivityDelegate",
+ QtAndroidPrivate::activity());
+ }
+ }
+
+ return m_activityDelegate;
}
- void setSystemUiVisibility(SystemUiVisibility uiVisibility)
+ QtJniTypes::QtInputDelegate qtInputDelegate()
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility));
+ if (!m_inputDelegate.isValid()) {
+ m_inputDelegate = qtActivityDelegate().callMethod<QtJniTypes::QtInputDelegate>(
+ "getInputDelegate");
+ }
+
+ return m_inputDelegate;
}
- void notifyAccessibilityLocationChange()
+ bool isQtApplication()
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange");
+ // Returns true if the app is a Qt app, i.e. Qt controls the whole app and
+ // the Activity/Service is created by Qt. Returns false if instead Qt is
+ // embedded into a native Android app, where the Activity/Service is created
+ // by the user, outside of Qt, and Qt content is added as a view.
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ auto activity = QtAndroidPrivate::activity();
+ if (activity.isValid())
+ return env->IsInstanceOf(activity.object(), m_qtActivityClass);
+ auto service = QtAndroidPrivate::service();
+ if (service.isValid())
+ return env->IsInstanceOf(QtAndroidPrivate::service().object(), m_qtServiceClass);
+ // return true as default as Qt application is our default use case.
+ // famous last words: we should not end up here
+ return true;
}
- void notifyObjectHide(uint accessibilityObjectId)
+ void notifyAccessibilityLocationChange(uint accessibilityObjectId)
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId);
+ qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId);
+ }
+
+ void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
+ {
+ qtActivityDelegate().callMethod<void>("notifyObjectHide",
+ accessibilityObjectId, parentObjectId);
+ }
+
+ void notifyObjectShow(uint parentObjectId)
+ {
+ qtActivityDelegate().callMethod<void>("notifyObjectShow",
+ parentObjectId);
}
void notifyObjectFocus(uint accessibilityObjectId)
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId);
+ qtActivityDelegate().callMethod<void>("notifyObjectFocus", accessibilityObjectId);
+ }
+
+ void notifyValueChanged(uint accessibilityObjectId, jstring value)
+ {
+ qtActivityDelegate().callMethod<void>("notifyValueChanged", accessibilityObjectId, value);
}
- void notifyQtAndroidPluginRunning(bool running)
+ void notifyScrolledEvent(uint accessibilityObjectId)
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running);
+ qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId);
+ }
+
+ void notifyNativePluginIntegrationReady(bool ready)
+ {
+ QJniObject::callStaticMethod<void>(m_applicationClass,
+ "notifyNativePluginIntegrationReady",
+ ready);
}
jobject createBitmap(QImage img, JNIEnv *env)
@@ -234,7 +279,7 @@ namespace QtAndroid
return 0;
if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16)
- img = img.convertToFormat(QImage::Format_RGBA8888);
+ img = std::move(img).convertToFormat(QImage::Format_RGBA8888);
jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass,
m_createBitmapMethodID,
@@ -319,58 +364,7 @@ namespace QtAndroid
QString manufacturer = QJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString();
QString model = QJniObject::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString();
- return manufacturer + QLatin1Char(' ') + model;
- }
-
- int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
- {
- QJniEnvironment env;
- if (!env.jniEnv())
- return -1;
-
- m_surfacesMutex.lock();
- int surfaceId = m_surfaceId++;
- m_surfaces[surfaceId] = client;
- m_surfacesMutex.unlock();
-
- jint x = 0, y = 0, w = -1, h = -1;
- if (!geometry.isNull()) {
- x = geometry.x();
- y = geometry.y();
- w = std::max(geometry.width(), 1);
- h = std::max(geometry.height(), 1);
- }
- env->CallStaticVoidMethod(m_applicationClass,
- m_createSurfaceMethodID,
- surfaceId,
- jboolean(onTop),
- x, y, w, h,
- imageDepth);
- return surfaceId;
- }
-
- int insertNativeView(jobject view, const QRect &geometry)
- {
- m_surfacesMutex.lock();
- const int surfaceId = m_surfaceId++;
- m_surfaces[surfaceId] = nullptr; // dummy
- m_surfacesMutex.unlock();
-
- jint x = 0, y = 0, w = -1, h = -1;
- if (!geometry.isNull())
- geometry.getRect(&x, &y, &w, &h);
-
- QJniObject::callStaticMethod<void>(m_applicationClass,
- "insertNativeView",
- "(ILandroid/view/View;IIII)V",
- surfaceId,
- view,
- x,
- y,
- qMax(w, 1),
- qMax(h, 1));
-
- return surfaceId;
+ return manufacturer + u' ' + model;
}
void setViewVisibility(jobject view, bool visible)
@@ -382,69 +376,6 @@ namespace QtAndroid
visible);
}
- void setSurfaceGeometry(int surfaceId, const QRect &geometry)
- {
- if (surfaceId == -1)
- return;
-
- QJniEnvironment env;
- if (!env.jniEnv())
- return;
- jint x = 0, y = 0, w = -1, h = -1;
- if (!geometry.isNull()) {
- x = geometry.x();
- y = geometry.y();
- w = geometry.width();
- h = geometry.height();
- }
- env->CallStaticVoidMethod(m_applicationClass,
- m_setSurfaceGeometryMethodID,
- surfaceId,
- x, y, w, h);
- }
-
-
- void destroySurface(int surfaceId)
- {
- if (surfaceId == -1)
- return;
-
- {
- QMutexLocker lock(&m_surfacesMutex);
- const auto &it = m_surfaces.find(surfaceId);
- if (it != m_surfaces.end())
- m_surfaces.erase(it);
- }
-
- QJniEnvironment env;
- if (env.jniEnv())
- env->CallStaticVoidMethod(m_applicationClass,
- m_destroySurfaceMethodID,
- surfaceId);
- }
-
- void bringChildToFront(int surfaceId)
- {
- if (surfaceId == -1)
- return;
-
- QJniObject::callStaticMethod<void>(m_applicationClass,
- "bringChildToFront",
- "(I)V",
- surfaceId);
- }
-
- void bringChildToBack(int surfaceId)
- {
- if (surfaceId == -1)
- return;
-
- QJniObject::callStaticMethod<void>(m_applicationClass,
- "bringChildToBack",
- "(I)V",
- surfaceId);
- }
-
bool blockEventLoopsWhenSuspended()
{
static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
@@ -458,29 +389,19 @@ namespace QtAndroid
} // namespace QtAndroid
-static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
+static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString)
{
+ Q_UNUSED(env)
+
m_androidPlatformIntegration = nullptr;
m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
m_mainLibraryHnd = nullptr;
- { // Set env. vars
- const char *nativeString = env->GetStringUTFChars(environmentString, 0);
- const QList<QByteArray> envVars = QByteArray(nativeString).split('\t');
- env->ReleaseStringUTFChars(environmentString, nativeString);
- for (const QByteArray &envVar : envVars) {
- int pos = envVar.indexOf('=');
- if (pos != -1 && ::setenv(envVar.left(pos), envVar.mid(pos + 1), 1) != 0)
- qWarning() << "Can't set environment" << envVar;
- }
- }
- const char *nativeString = env->GetStringUTFChars(paramsString, 0);
- QByteArray string = nativeString;
- env->ReleaseStringUTFChars(paramsString, nativeString);
+ const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
- for (auto str : string.split('\t'))
- m_applicationParams.append(str.split(' '));
+ for (const QString &arg : argsList)
+ m_applicationParams.append(arg.toUtf8());
// Go home
QDir::setCurrent(QDir::homePath());
@@ -520,7 +441,7 @@ static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
Q_UNUSED(env);
// The service must wait until the QCoreApplication starts otherwise onBind will be
// called too early
- if (m_serviceObject)
+ if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication())
QtAndroidPrivate::waitForServiceSetup();
}
@@ -537,15 +458,22 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
vm->AttachCurrentThread(&env, &args);
}
+ // Register type for invokeMethod() calls.
+ qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
+
// Register resources if they are available
if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
- QVarLengthArray<const char *> params(m_applicationParams.size());
- for (int i = 0; i < m_applicationParams.size(); i++)
- params[i] = static_cast<const char *>(m_applicationParams[i].constData());
+ const int argc = m_applicationParams.size();
+ QVarLengthArray<char *> argv(argc + 1);
+ for (int i = 0; i < argc; i++)
+ argv[i] = m_applicationParams[i].data();
+ argv[argc] = nullptr;
- int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
+ startQtAndroidPluginCalled.fetchAndAddRelease(1);
+ const int ret = m_main(argc, argv.data());
+ qInfo() << "main() returned" << ret;
if (m_mainLibraryHnd) {
int res = dlclose(m_mainLibraryHnd);
@@ -553,10 +481,8 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
qWarning() << "dlclose failed:" << dlerror();
}
- if (m_applicationClass) {
- qWarning("exit app 0");
+ if (m_applicationClass)
QJniObject::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
- }
sem_post(&m_terminateSemaphore);
sem_wait(&m_exitSemaphore);
@@ -592,17 +518,15 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
QAndroidEventDispatcherStopper::instance()->goingToStop(false);
}
- sem_wait(&m_terminateSemaphore);
+ if (startQtAndroidPluginCalled.loadAcquire())
+ sem_wait(&m_terminateSemaphore);
+
sem_destroy(&m_terminateSemaphore);
env->DeleteGlobalRef(m_applicationClass);
env->DeleteGlobalRef(m_classLoaderObject);
if (m_resourcesObj)
env->DeleteGlobalRef(m_resourcesObj);
- if (m_activityObject)
- env->DeleteGlobalRef(m_activityObject);
- if (m_serviceObject)
- env->DeleteGlobalRef(m_serviceObject);
if (m_bitmapClass)
env->DeleteGlobalRef(m_bitmapClass);
if (m_ARGB_8888_BitmapConfigValue)
@@ -613,54 +537,49 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
env->DeleteGlobalRef(m_bitmapDrawableClass);
if (m_assets)
env->DeleteGlobalRef(m_assets);
+ if (m_qtActivityClass)
+ env->DeleteGlobalRef(m_qtActivityClass);
+ if (m_qtServiceClass)
+ env->DeleteGlobalRef(m_qtServiceClass);
m_androidPlatformIntegration = nullptr;
delete m_androidAssetsFileEngineHandler;
m_androidAssetsFileEngineHandler = nullptr;
sem_post(&m_exitSemaphore);
}
-static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h)
+static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels,
+ jint screenHeightPixels, jint availableLeftPixels,
+ jint availableTopPixels, jint availableWidthPixels,
+ jint availableHeightPixels, jdouble xdpi, jdouble ydpi,
+ jdouble scaledDensity, jdouble density, jfloat refreshRate)
{
- QMutexLocker lock(&m_surfacesMutex);
- const auto &it = m_surfaces.find(id);
- if (it == m_surfaces.end())
- return;
+ Q_UNUSED(availableLeftPixels)
+ Q_UNUSED(availableTopPixels)
- auto surfaceClient = it.value();
- if (surfaceClient)
- surfaceClient->surfaceChanged(env, jSurface, w, h);
-}
-
-static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
- jint screenWidthPixels, jint screenHeightPixels,
- jint availableLeftPixels, jint availableTopPixels,
- jint availableWidthPixels, jint availableHeightPixels,
- jdouble xdpi, jdouble ydpi,
- jdouble scaledDensity, jdouble density)
-{
m_availableWidthPixels = availableWidthPixels;
m_availableHeightPixels = availableHeightPixels;
m_scaledDensity = scaledDensity;
m_density = density;
+ const QSize screenSize(screenWidthPixels, screenHeightPixels);
+ // available geometry always starts from top left
+ const QRect availableGeometry(0, 0, availableWidthPixels, availableHeightPixels);
+ const QSize physicalSize(qRound(double(screenWidthPixels) / xdpi * 25.4),
+ qRound(double(screenHeightPixels) / ydpi * 25.4));
+
QMutexLocker lock(&m_platformMutex);
if (!m_androidPlatformIntegration) {
- QAndroidPlatformIntegration::setDefaultDisplayMetrics(availableLeftPixels,
- availableTopPixels,
- availableWidthPixels,
- availableHeightPixels,
- qRound(double(screenWidthPixels) / xdpi * 25.4),
- qRound(double(screenHeightPixels) / ydpi * 25.4),
- screenWidthPixels,
- screenHeightPixels);
+ QAndroidPlatformIntegration::setDefaultDisplayMetrics(
+ availableGeometry.left(), availableGeometry.top(), availableGeometry.width(),
+ availableGeometry.height(), physicalSize.width(), physicalSize.height(),
+ screenSize.width(), screenSize.height());
} else {
- m_androidPlatformIntegration->setPhysicalSize(qRound(double(screenWidthPixels) / xdpi * 25.4),
- qRound(double(screenHeightPixels) / ydpi * 25.4));
- m_androidPlatformIntegration->setScreenSize(screenWidthPixels, screenHeightPixels);
- m_androidPlatformIntegration->setAvailableGeometry(QRect(availableLeftPixels, availableTopPixels,
- availableWidthPixels, availableHeightPixels));
+ m_androidPlatformIntegration->setScreenSizeParameters(physicalSize, screenSize,
+ availableGeometry);
+ m_androidPlatformIntegration->setRefreshRate(refreshRate);
}
}
+Q_DECLARE_JNI_NATIVE_METHOD(setDisplayMetrics)
static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/)
{
@@ -680,10 +599,6 @@ static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/)
QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size())));
}
}
-
- QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen());
- if (screen->rasterSurfaces())
- QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry()));
}
static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
@@ -753,11 +668,51 @@ static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint new
QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
QMutexLocker lock(&m_platformMutex);
if (m_androidPlatformIntegration) {
- QPlatformScreen *screen = m_androidPlatformIntegration->screen();
- QWindowSystemInterface::handleScreenOrientationChange(screen->screen(),
- screenOrientation);
+ QAndroidPlatformScreen *screen = m_androidPlatformIntegration->screen();
+ // Use invokeMethod to keep the certain order of the "geometry change"
+ // and "orientation change" event handling.
+ if (screen) {
+ QMetaObject::invokeMethod(screen, "setOrientation", Qt::AutoConnection,
+ Q_ARG(Qt::ScreenOrientation, screenOrientation));
+ }
}
}
+Q_DECLARE_JNI_NATIVE_METHOD(handleOrientationChanged)
+
+static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate)
+{
+ if (m_androidPlatformIntegration)
+ m_androidPlatformIntegration->setRefreshRate(refreshRate);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(handleRefreshRateChanged)
+
+static void handleScreenAdded(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
+{
+ if (m_androidPlatformIntegration)
+ m_androidPlatformIntegration->handleScreenAdded(displayId);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(handleScreenAdded)
+
+static void handleScreenChanged(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
+{
+ if (m_androidPlatformIntegration)
+ m_androidPlatformIntegration->handleScreenChanged(displayId);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(handleScreenChanged)
+
+static void handleScreenRemoved(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
+{
+ if (m_androidPlatformIntegration)
+ m_androidPlatformIntegration->handleScreenRemoved(displayId);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(handleScreenRemoved)
+
+static void handleUiDarkModeChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newUiMode)
+{
+ QAndroidPlatformIntegration::updateColorScheme(
+ (newUiMode == 1 ) ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light);
+}
+Q_DECLARE_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
jint requestCode,
@@ -778,117 +733,137 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
}
static JNINativeMethod methods[] = {
- {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin},
- {"startQtApplication", "()V", (void *)startQtApplication},
- {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin},
- {"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
- {"terminateQt", "()V", (void *)terminateQt},
- {"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
- {"setDisplayMetrics", "(IIIIIIDDDD)V", (void *)setDisplayMetrics},
- {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
- {"updateWindow", "()V", (void *)updateWindow},
- {"updateApplicationState", "(I)V", (void *)updateApplicationState},
- {"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged},
- {"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult},
- {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent},
- {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind}
+ { "startQtAndroidPlugin", "(Ljava/lang/String;)Z", (void *)startQtAndroidPlugin },
+ { "startQtApplication", "()V", (void *)startQtApplication },
+ { "quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin },
+ { "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication },
+ { "terminateQt", "()V", (void *)terminateQt },
+ { "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
+ { "updateWindow", "()V", (void *)updateWindow },
+ { "updateApplicationState", "(I)V", (void *)updateApplicationState },
+ { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },
+ { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent },
+ { "onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind }
};
#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
clazz = env->FindClass(CLASS_NAME); \
if (!clazz) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
- return JNI_FALSE; \
+ return false; \
}
#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
- return JNI_FALSE; \
+ return false; \
}
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
- return JNI_FALSE; \
+ return false; \
}
#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
- return JNI_FALSE; \
+ return false; \
}
#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
if (!VAR) { \
__android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
- return JNI_FALSE; \
+ return false; \
}
-static int registerNatives(JNIEnv *env)
+Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
+
+static bool registerNatives(QJniEnvironment &env)
{
jclass clazz;
FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNative");
m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
- if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+ if (!env.registerNativeMethods(m_applicationClass,
+ methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
- return JNI_FALSE;
+ return false;
}
- GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V");
- GET_AND_CHECK_STATIC_METHOD(m_setSurfaceGeometryMethodID, m_applicationClass, "setSurfaceGeometry", "(IIIII)V");
- GET_AND_CHECK_STATIC_METHOD(m_destroySurfaceMethodID, m_applicationClass, "destroySurface", "(I)V");
+ bool success = env.registerNativeMethods(
+ QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(),
+ {
+ Q_JNI_NATIVE_METHOD(setDisplayMetrics),
+ Q_JNI_NATIVE_METHOD(handleOrientationChanged),
+ Q_JNI_NATIVE_METHOD(handleRefreshRateChanged),
+ Q_JNI_NATIVE_METHOD(handleScreenAdded),
+ Q_JNI_NATIVE_METHOD(handleScreenChanged),
+ Q_JNI_NATIVE_METHOD(handleScreenRemoved),
+ Q_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
+ });
+
+ if (!success) {
+ qCritical() << "QtDisplayManager: registerNativeMethods() failed";
+ return JNI_FALSE;
+ }
jmethodID methodID;
GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
- jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
- GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
- jobject serviceObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
+ jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
+ if (!contextObject) {
+ GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
+ contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
+ }
+
+ if (!contextObject) {
+ __android_log_print(ANDROID_LOG_FATAL,"Qt", "Failed to get Activity or Service object");
+ return false;
+ }
+ const auto releaseContextObject = qScopeGuard([&env, contextObject]{
+ env->DeleteLocalRef(contextObject);
+ });
+
GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;");
m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID));
clazz = env->GetObjectClass(m_classLoaderObject);
GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
- if (serviceObject)
- m_serviceObject = env->NewGlobalRef(serviceObject);
-
- if (activityObject)
- m_activityObject = env->NewGlobalRef(activityObject);
-
- jobject object = activityObject ? activityObject : serviceObject;
- if (object) {
- FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
- GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
- m_assets = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
- m_assetManager = AAssetManager_fromJava(env, m_assets);
-
- GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
- m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
-
- FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
- m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
- GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass
- , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
- FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
- jfieldID fieldId;
- GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
- m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
- GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
- m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
-
- FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
- m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
- GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
- m_bitmapDrawableClass,
- "<init>",
- "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
- }
-
- return JNI_TRUE;
+
+ FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
+ GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
+ m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
+ m_assetManager = AAssetManager_fromJava(env.jniEnv(), m_assets);
+
+ GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
+ m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
+
+ FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
+ m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass,
+ "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
+ FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
+ jfieldID fieldId;
+ GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
+ m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
+ GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
+ m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
+
+ FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
+ m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
+ m_bitmapDrawableClass,
+ "<init>", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
+
+ FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtActivityBase");
+ m_qtActivityClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtServiceBase");
+ m_qtServiceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+ return true;
}
QT_END_NAMESPACE
@@ -901,36 +876,29 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
initialized = true;
QT_USE_NAMESPACE
- typedef union {
- JNIEnv *nativeEnvironment;
- void *venv;
- } UnionJNIEnvToVoid;
-
- UnionJNIEnvToVoid uenv;
- uenv.venv = nullptr;
- m_javaVM = nullptr;
-
- if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
- __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed");
+ m_javaVM = vm;
+ QJniEnvironment env;
+ if (!env.isValid()) {
+ m_javaVM = nullptr;
+ __android_log_print(ANDROID_LOG_FATAL, "Qt", "Failed to initialize the JNI Environment");
return -1;
}
- JNIEnv *env = uenv.nativeEnvironment;
if (!registerNatives(env)
|| !QtAndroidInput::registerNatives(env)
|| !QtAndroidMenu::registerNatives(env)
|| !QtAndroidAccessibility::registerNatives(env)
- || !QtAndroidDialogHelpers::registerNatives(env)) {
+ || !QtAndroidDialogHelpers::registerNatives(env)
+ || !QAndroidPlatformClipboard::registerNatives(env)
+ || !QAndroidPlatformWindow::registerNatives(env)
+ || !QtAndroidWindowEmbedding::registerNatives(env)) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1;
}
QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
- m_javaVM = vm;
// attach qt main thread data to this thread
- QObject threadSetter;
- if (threadSetter.thread())
- threadSetter.thread()->setObjectName("QtMainLoopThread");
+ QThread::currentThread()->setObjectName("QtMainLoopThread");
__android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
return JNI_VERSION_1_6;
}
diff --git a/src/plugins/platforms/android/androidjnimain.h b/src/plugins/platforms/android/androidjnimain.h
index c7dd5c0ca0..99fff96d2b 100644
--- a/src/plugins/platforms/android/androidjnimain.h
+++ b/src/plugins/platforms/android/androidjnimain.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROID_APP_H
#define ANDROID_APP_H
@@ -47,6 +11,8 @@
#include <android/asset_manager.h>
#include <QImage>
+#include <private/qjnihelpers_p.h>
+#include <QtCore/QJniObject>
QT_BEGIN_NAMESPACE
@@ -57,26 +23,22 @@ class QAndroidPlatformIntegration;
class QWidget;
class QString;
class QWindow;
-class AndroidSurfaceClient;
+class QAndroidPlatformWindow;
class QBasicMutex;
+Q_DECLARE_JNI_CLASS(QtActivityDelegateBase, "org/qtproject/qt/android/QtActivityDelegateBase")
+Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate")
+
namespace QtAndroid
{
QBasicMutex *platformInterfaceMutex();
QAndroidPlatformIntegration *androidPlatformIntegration();
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration);
void setQtThread(QThread *thread);
-
-
- int createSurface(AndroidSurfaceClient * client, const QRect &geometry, bool onTop, int imageDepth);
- int insertNativeView(jobject view, const QRect &geometry);
void setViewVisibility(jobject view, bool visible);
- void setSurfaceGeometry(int surfaceId, const QRect &geometry);
- void destroySurface(int surfaceId);
- void bringChildToFront(int surfaceId);
- void bringChildToBack(int surfaceId);
QWindow *topLevelWindowAt(const QPoint &globalPos);
+ QWindow *windowFromId(int windowId);
int availableWidthPixels();
int availableHeightPixels();
double scaledDensity();
@@ -85,8 +47,9 @@ namespace QtAndroid
jobject assets();
AAssetManager *assetManager();
jclass applicationClass();
- jobject activity();
- jobject service();
+
+ QtJniTypes::QtActivityDelegateBase qtActivityDelegate();
+ QtJniTypes::QtInputDelegate qtInputDelegate();
// Keep synchronized with flags in ActivityDelegate.java
enum SystemUiVisibility {
@@ -100,10 +63,13 @@ namespace QtAndroid
jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env);
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env = nullptr);
- void notifyAccessibilityLocationChange();
- void notifyObjectHide(uint accessibilityObjectId);
+ void notifyAccessibilityLocationChange(uint accessibilityObjectId);
+ void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId);
+ void notifyObjectShow(uint parentObjectId);
void notifyObjectFocus(uint accessibilityObjectId);
- void notifyQtAndroidPluginRunning(bool running);
+ void notifyValueChanged(uint accessibilityObjectId, jstring value);
+ void notifyScrolledEvent(uint accessibilityObjectId);
+ void notifyNativePluginIntegrationReady(bool ready);
const char *classErrorMsgFmt();
const char *methodErrorMsgFmt();
@@ -111,6 +77,8 @@ namespace QtAndroid
QString deviceName();
bool blockEventLoopsWhenSuspended();
+
+ bool isQtApplication();
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidjnimenu.cpp b/src/plugins/platforms/android/androidjnimenu.cpp
index 9b49140335..8bf37d1af2 100644
--- a/src/plugins/platforms/android/androidjnimenu.cpp
+++ b/src/plugins/platforms/android/androidjnimenu.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidjnimain.h"
#include "androidjnimenu.h"
@@ -60,14 +24,12 @@ namespace QtAndroidMenu
{
static QList<QAndroidPlatformMenu *> pendingContextMenus;
static QAndroidPlatformMenu *visibleMenu = nullptr;
- static QRecursiveMutex visibleMenuMutex;
+ Q_CONSTINIT static QRecursiveMutex visibleMenuMutex;
static QSet<QAndroidPlatformMenuBar *> menuBars;
static QAndroidPlatformMenuBar *visibleMenuBar = nullptr;
static QWindow *activeTopLevelWindow = nullptr;
- static QRecursiveMutex menuBarMutex;
-
- static jmethodID openContextMenuMethodID = 0;
+ Q_CONSTINIT static QRecursiveMutex menuBarMutex;
static jmethodID clearMenuMethodID = 0;
static jmethodID addMenuItemMethodID = 0;
@@ -82,29 +44,31 @@ namespace QtAndroidMenu
void resetMenuBar()
{
- QJniObject::callStaticMethod<void>(applicationClass(), "resetOptionsMenu");
+ qtActivityDelegate().callMethod<void>("resetOptionsMenu");
}
void openOptionsMenu()
{
- QJniObject::callStaticMethod<void>(applicationClass(), "openOptionsMenu");
+ qtActivityDelegate().callMethod<void>("openOptionsMenu");
}
- void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect, JNIEnv *env)
+ void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect)
{
QMutexLocker lock(&visibleMenuMutex);
if (visibleMenu)
pendingContextMenus.append(visibleMenu);
visibleMenu = menu;
menu->aboutToShow();
- env->CallStaticVoidMethod(applicationClass(), openContextMenuMethodID, anchorRect.x(), anchorRect.y(), anchorRect.width(), anchorRect.height());
+ qtActivityDelegate().callMethod<void>("openContextMenu",
+ anchorRect.x(), anchorRect.y(),
+ anchorRect.width(), anchorRect.height());
}
void hideContextMenu(QAndroidPlatformMenu *menu)
{
QMutexLocker lock(&visibleMenuMutex);
if (visibleMenu == menu) {
- QJniObject::callStaticMethod<void>(applicationClass(), "closeContextMenu");
+ qtActivityDelegate().callMethod<void>("closeContextMenu");
pendingContextMenus.clear();
} else {
pendingContextMenus.removeOne(menu);
@@ -153,7 +117,7 @@ namespace QtAndroidMenu
visibleMenuBar = 0;
activeTopLevelWindow = window;
- for (QAndroidPlatformMenuBar *menuBar : qAsConst(menuBars)) {
+ for (QAndroidPlatformMenuBar *menuBar : std::as_const(menuBars)) {
if (menuBar->parentWindow() == window) {
visibleMenuBar = menuBar;
resetMenuBar();
@@ -181,12 +145,12 @@ namespace QtAndroidMenu
static QString removeAmpersandEscapes(QString s)
{
- int i = 0;
+ qsizetype i = 0;
while (i < s.size()) {
++i;
- if (s.at(i-1) != QLatin1Char('&'))
+ if (s.at(i - 1) != u'&')
continue;
- if (i < s.size() && s.at(i) == QLatin1Char('&'))
+ if (i < s.size() && s.at(i) == u'&')
++i;
s.remove(i-1,1);
}
@@ -247,8 +211,10 @@ namespace QtAndroidMenu
return order;
}
- static jboolean onPrepareOptionsMenu(JNIEnv *env, jobject /*thiz*/, jobject menu)
+ static jboolean onPrepareOptionsMenu(JNIEnv *env, jobject thiz, jobject menu)
{
+ Q_UNUSED(thiz)
+
env->CallVoidMethod(menu, clearMenuMethodID);
QMutexLocker lock(&menuBarMutex);
if (!visibleMenuBar)
@@ -285,8 +251,11 @@ namespace QtAndroidMenu
return order ? JNI_TRUE : JNI_FALSE;
}
- static jboolean onOptionsItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked)
+ static jboolean onOptionsItemSelected(JNIEnv *env, jobject thiz, jint menuId, jboolean checked)
{
+ Q_UNUSED(env)
+ Q_UNUSED(thiz)
+
QMutexLocker lock(&menuBarMutex);
if (!visibleMenuBar)
return JNI_FALSE;
@@ -296,7 +265,7 @@ namespace QtAndroidMenu
QAndroidPlatformMenuItem *item = static_cast<QAndroidPlatformMenuItem *>(menus.front()->menuItemForId(menuId));
if (item) {
if (item->menu()) {
- showContextMenu(item->menu(), QRect(), env);
+ showContextMenu(item->menu(), QRect());
} else {
if (item->isCheckable())
item->setChecked(checked);
@@ -306,18 +275,23 @@ namespace QtAndroidMenu
} else {
QAndroidPlatformMenu *menu = static_cast<QAndroidPlatformMenu *>(visibleMenuBar->menuForId(menuId));
if (menu)
- showContextMenu(menu, QRect(), env);
+ showContextMenu(menu, QRect());
}
return JNI_TRUE;
}
- static void onOptionsMenuClosed(JNIEnv */*env*/, jobject /*thiz*/, jobject /*menu*/)
+ static void onOptionsMenuClosed(JNIEnv *env, jobject thiz, jobject menu)
{
+ Q_UNUSED(env)
+ Q_UNUSED(thiz)
+ Q_UNUSED(menu)
}
- static void onCreateContextMenu(JNIEnv *env, jobject /*thiz*/, jobject menu)
+ static void onCreateContextMenu(JNIEnv *env, jobject thiz, jobject menu)
{
+ Q_UNUSED(thiz)
+
env->CallVoidMethod(menu, clearMenuMethodID);
QMutexLocker lock(&visibleMenuMutex);
if (!visibleMenu)
@@ -331,8 +305,9 @@ namespace QtAndroidMenu
addAllMenuItemsToMenu(env, menu, visibleMenu);
}
- static void fillContextMenu(JNIEnv *env, jobject /*thiz*/, jobject menu)
+ static void fillContextMenu(JNIEnv *env, jobject thiz, jobject menu)
{
+ Q_UNUSED(thiz)
env->CallVoidMethod(menu, clearMenuMethodID);
QMutexLocker lock(&visibleMenuMutex);
if (!visibleMenu)
@@ -341,20 +316,23 @@ namespace QtAndroidMenu
addAllMenuItemsToMenu(env, menu, visibleMenu);
}
- static jboolean onContextItemSelected(JNIEnv *env, jobject /*thiz*/, jint menuId, jboolean checked)
+ static jboolean onContextItemSelected(JNIEnv *env, jobject thiz, jint menuId, jboolean checked)
{
+ Q_UNUSED(env)
+ Q_UNUSED(thiz)
+
QMutexLocker lock(&visibleMenuMutex);
QAndroidPlatformMenuItem * item = static_cast<QAndroidPlatformMenuItem *>(visibleMenu->menuItemForId(menuId));
if (item) {
if (item->menu()) {
- showContextMenu(item->menu(), QRect(), env);
+ showContextMenu(item->menu(), QRect());
} else {
if (item->isCheckable())
item->setChecked(checked);
item->activated();
visibleMenu->aboutToHide();
visibleMenu = 0;
- for (QAndroidPlatformMenu *menu : qAsConst(pendingContextMenus)) {
+ for (QAndroidPlatformMenu *menu : std::as_const(pendingContextMenus)) {
if (menu->isVisible())
menu->aboutToHide();
}
@@ -364,8 +342,12 @@ namespace QtAndroidMenu
return JNI_TRUE;
}
- static void onContextMenuClosed(JNIEnv *env, jobject /*thiz*/, jobject /*menu*/)
+ static void onContextMenuClosed(JNIEnv *env, jobject thiz, jobject menu)
{
+ Q_UNUSED(env)
+ Q_UNUSED(thiz)
+ Q_UNUSED(menu)
+
QMutexLocker lock(&visibleMenuMutex);
if (!visibleMenu)
return;
@@ -373,7 +355,7 @@ namespace QtAndroidMenu
visibleMenu->aboutToHide();
visibleMenu = 0;
if (!pendingContextMenus.empty())
- showContextMenu(pendingContextMenus.takeLast(), QRect(), env);
+ showContextMenu(pendingContextMenus.takeLast(), QRect());
}
static JNINativeMethod methods[] = {
@@ -414,17 +396,15 @@ namespace QtAndroidMenu
return false; \
}
- bool registerNatives(JNIEnv *env)
+ bool registerNatives(QJniEnvironment &env)
{
jclass appClass = applicationClass();
- if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+ if (!env.registerNativeMethods(appClass, methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
return false;
}
- GET_AND_CHECK_STATIC_METHOD(openContextMenuMethodID, appClass, "openContextMenu", "(IIII)V");
-
jclass clazz;
FIND_AND_CHECK_CLASS("android/view/Menu");
GET_AND_CHECK_METHOD(clearMenuMethodID, clazz, "clear", "()V");
diff --git a/src/plugins/platforms/android/androidjnimenu.h b/src/plugins/platforms/android/androidjnimenu.h
index 9569d18978..e10ad930d9 100644
--- a/src/plugins/platforms/android/androidjnimenu.h
+++ b/src/plugins/platforms/android/androidjnimenu.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDJNIMENU_H
#define ANDROIDJNIMENU_H
@@ -51,12 +15,13 @@ class QAndroidPlatformMenuItem;
class QWindow;
class QRect;
class QPoint;
+class QJniEnvironment;
namespace QtAndroidMenu
{
// Menu support
void openOptionsMenu();
- void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect, JNIEnv *env);
+ void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect);
void hideContextMenu(QAndroidPlatformMenu *menu);
void syncMenu(QAndroidPlatformMenu *menu);
void androidPlatformMenuDestroyed(QAndroidPlatformMenu *menu);
@@ -67,7 +32,7 @@ namespace QtAndroidMenu
void removeMenuBar(QAndroidPlatformMenuBar *menuBar);
// Menu support
- bool registerNatives(JNIEnv *env);
+ bool registerNatives(QJniEnvironment &env);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidsurfaceclient.h b/src/plugins/platforms/android/androidsurfaceclient.h
deleted file mode 100644
index f06106f0b7..0000000000
--- a/src/plugins/platforms/android/androidsurfaceclient.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef ANDROIDSURFACECLIENT_H
-#define ANDROIDSURFACECLIENT_H
-#include <QMutex>
-#include <jni.h>
-
-QT_BEGIN_NAMESPACE
-
-class AndroidSurfaceClient
-{
-public:
- virtual void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) = 0;
- void lockSurface() { m_surfaceMutex.lock(); }
- void unlockSurface() { m_surfaceMutex.unlock(); }
-
-protected:
- QMutex m_surfaceMutex;
-};
-
-QT_END_NAMESPACE
-
-#endif // ANDROIDSURFACECLIENT_H
diff --git a/src/plugins/platforms/android/androidwindowembedding.cpp b/src/plugins/platforms/android/androidwindowembedding.cpp
new file mode 100644
index 0000000000..65dabcac66
--- /dev/null
+++ b/src/plugins/platforms/android/androidwindowembedding.cpp
@@ -0,0 +1,70 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "androidwindowembedding.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qjnitypes.h>
+#include <QtGui/qwindow.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_JNI_CLASS(QtView, "org/qtproject/qt/android/QtView");
+
+namespace QtAndroidWindowEmbedding {
+ void createRootWindow(JNIEnv *, jclass, QtJniTypes::View rootView,
+ jint x, jint y, jint width, jint height)
+ {
+ // QWindow should be constructed on the Qt thread rather than directly in the caller thread
+ // To avoid hitting checkReceiverThread assert in QCoreApplication::doNotify
+ QMetaObject::invokeMethod(qApp, [rootView, x, y, width, height] {
+ QWindow *parentWindow = QWindow::fromWinId(reinterpret_cast<WId>(rootView.object()));
+ parentWindow->setGeometry(x, y, width, height);
+ rootView.callMethod<void>("createWindow", reinterpret_cast<jlong>(parentWindow));
+ });
+ }
+
+ void deleteWindow(JNIEnv *, jclass, jlong windowRef)
+ {
+ QWindow *window = reinterpret_cast<QWindow*>(windowRef);
+ window->deleteLater();
+ }
+
+ void setWindowVisible(JNIEnv *, jclass, jlong windowRef, jboolean visible)
+ {
+ QMetaObject::invokeMethod(qApp, [windowRef, visible] {
+ QWindow *window = reinterpret_cast<QWindow*>(windowRef);
+ if (visible) {
+ window->showNormal();
+ if (!window->parent()->isVisible())
+ window->parent()->showNormal();
+ } else {
+ window->hide();
+ }
+ });
+ }
+
+ void resizeWindow(JNIEnv *, jclass, jlong windowRef, jint x, jint y, jint width, jint height)
+ {
+ QMetaObject::invokeMethod(qApp, [windowRef, x, y, width, height] {
+ QWindow *window = reinterpret_cast<QWindow*>(windowRef);
+ QWindow *parent = window->parent();
+ if (parent)
+ parent->setGeometry(x, y, width, height);
+ window->setGeometry(0, 0, width, height);
+ });
+ }
+
+ bool registerNatives(QJniEnvironment& env) {
+ return env.registerNativeMethods(
+ QtJniTypes::Traits<QtJniTypes::QtView>::className(),
+ { Q_JNI_NATIVE_SCOPED_METHOD(createRootWindow, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(deleteWindow, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(setWindowVisible, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(resizeWindow, QtAndroidWindowEmbedding) });
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/androidwindowembedding.h b/src/plugins/platforms/android/androidwindowembedding.h
new file mode 100644
index 0000000000..b7b0e1205f
--- /dev/null
+++ b/src/plugins/platforms/android/androidwindowembedding.h
@@ -0,0 +1,41 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTANDROIDWINDOWEMBEDDING_H
+#define QTANDROIDWINDOWEMBEDDING_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjnitypes.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_JNI_CLASS(View, "android/view/View");
+
+namespace QtAndroidWindowEmbedding
+{
+ bool registerNatives(QJniEnvironment& env);
+ void createRootWindow(JNIEnv *, jclass, QtJniTypes::View rootView,
+ jint x, jint y,jint width, jint height);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(createRootWindow)
+ void deleteWindow(JNIEnv *, jclass, jlong window);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(deleteWindow)
+ void setWindowVisible(JNIEnv *, jclass, jlong window, jboolean visible);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(setWindowVisible)
+ void resizeWindow(JNIEnv *, jclass, jlong windowRef, jint x, jint y, jint width, jint height);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(resizeWindow)
+};
+
+QT_END_NAMESPACE
+
+#endif // QTANDROIDWINDOWEMBEDDING_H
diff --git a/src/plugins/platforms/android/extract-dummy.cpp b/src/plugins/platforms/android/extract-dummy.cpp
index bfc0dafd04..a8b27bb0d8 100644
--- a/src/plugins/platforms/android/extract-dummy.cpp
+++ b/src/plugins/platforms/android/extract-dummy.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <jni.h>
#include <extract.h>
@@ -45,9 +9,3 @@ Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(JNIEnv *, jo
{
return 0;
}
-
-extern "C" JNIEXPORT jintArray JNICALL
-Java_org_qtproject_qt_android_ExtractStyle_extractChunkInfo20(JNIEnv *, jobject, jbyteArray)
-{
- return 0;
-}
diff --git a/src/plugins/platforms/android/extract.cpp b/src/plugins/platforms/android/extract.cpp
index cad54d580b..d81d70d18d 100644
--- a/src/plugins/platforms/android/extract.cpp
+++ b/src/plugins/platforms/android/extract.cpp
@@ -1,41 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
@@ -126,26 +91,6 @@ Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(JNIEnv *env,
return result;
}
-extern "C" JNIEXPORT jintArray JNICALL
-Java_org_qtproject_qt_android_ExtractStyle_extractChunkInfo20(JNIEnv *env, jobject obj,
- jbyteArray chunkObj)
-{
- size_t chunkSize = env->GetArrayLength(chunkObj);
- void* storage = alloca(chunkSize);
- env->GetByteArrayRegion(chunkObj, 0, chunkSize,
- reinterpret_cast<jbyte*>(storage));
-
- if (QJniEnvironment::checkAndClearExceptions(env))
- return 0;
-
- jintArray res = Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(env, obj,
- long(storage));
- if (QJniEnvironment::checkAndClearExceptions(env))
- res = nullptr;
-
- return res;
-}
-
static inline void fill9patchOffsets(Res_png_9patch20* patch) {
patch->xDivsOffset = sizeof(Res_png_9patch20);
patch->yDivsOffset = patch->xDivsOffset + (patch->numXDivs * sizeof(int32_t));
diff --git a/src/plugins/platforms/android/main.cpp b/src/plugins/platforms/android/main.cpp
index 4841d0425c..0e749f55a5 100644
--- a/src/plugins/platforms/android/main.cpp
+++ b/src/plugins/platforms/android/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qpa/qplatformintegrationplugin.h>
@@ -43,6 +7,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QAndroidIntegrationPlugin : public QPlatformIntegrationPlugin
{
Q_OBJECT
@@ -54,7 +20,7 @@ public:
QPlatformIntegration *QAndroidIntegrationPlugin::create(const QString& system, const QStringList& paramList)
{
- if (!system.compare(QLatin1String("android"), Qt::CaseInsensitive))
+ if (!system.compare("android"_L1, Qt::CaseInsensitive))
return new QAndroidPlatformIntegration(paramList);
return nullptr;
diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
index 15c0aada95..4ea6536cef 100644
--- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
+++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidjnimain.h"
#include "qandroidassetsfileenginehandler.h"
@@ -49,25 +13,27 @@
QT_BEGIN_NAMESPACE
-static const QLatin1String assetsPrefix("assets:");
+using namespace Qt::StringLiterals;
+
+static const auto assetsPrefix = "assets:"_L1;
const static int prefixSize = 7;
static inline QString cleanedAssetPath(QString file)
{
if (file.startsWith(assetsPrefix))
file.remove(0, prefixSize);
- file.replace(QLatin1String("//"), QLatin1String("/"));
- if (file.startsWith(QLatin1Char('/')))
+ file.replace("//"_L1, "/"_L1);
+ if (file.startsWith(u'/'))
file.remove(0, 1);
- if (file.endsWith(QLatin1Char('/')))
+ if (file.endsWith(u'/'))
file.chop(1);
return file;
}
static inline QString prefixedPath(QString path)
{
- path = assetsPrefix + QLatin1Char('/') + path;
- path.replace(QLatin1String("//"), QLatin1String("/"));
+ path = assetsPrefix + u'/' + path;
+ path.replace("//"_L1, "/"_L1);
return path;
}
@@ -81,13 +47,14 @@ struct AssetItem {
AssetItem (const QString &rawName)
: name(rawName)
{
- if (name.endsWith(QLatin1Char('/'))) {
+ if (name.endsWith(u'/')) {
type = Type::Folder;
name.chop(1);
}
}
Type type = Type::File;
QString name;
+ qint64 size = -1;
};
using AssetItemList = QList<AssetItem>;
@@ -114,7 +81,7 @@ public:
{
if (filePath.isEmpty())
return AssetItem::Type::Folder;
- const QStringList paths = filePath.split(QLatin1Char('/'));
+ const QStringList paths = filePath.split(u'/');
QString fullPath;
AssetItem::Type res = AssetItem::Type::Invalid;
for (const auto &path: paths) {
@@ -125,7 +92,7 @@ public:
if (it == folder->end() || it->name != path)
return AssetItem::Type::Invalid;
if (!fullPath.isEmpty())
- fullPath.append(QLatin1Char('/'));
+ fullPath.append(u'/');
fullPath += path;
res = it->type;
}
@@ -141,6 +108,8 @@ public:
FolderIterator(const QString &path)
: m_path(path)
{
+ // Note that empty dirs in the assets dir before the build are not going to be
+ // included in the final apk, so no empty folders should expected to be listed.
QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(),
"listAssetContent",
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
@@ -156,8 +125,8 @@ public:
}), item);
}
}
- m_path = assetsPrefix + QLatin1Char('/') + m_path + QLatin1Char('/');
- m_path.replace(QLatin1String("//"), QLatin1String("/"));
+ m_path = assetsPrefix + u'/' + m_path + u'/';
+ m_path.replace("//"_L1, "/"_L1);
}
QString currentFileName() const
@@ -173,17 +142,13 @@ public:
return m_path + at(m_index).name;
}
- bool hasNext() const
- {
- return !empty() && m_index + 1 < size();
- }
-
- std::optional<std::pair<QString, AssetItem>> next()
+ bool advance()
{
- if (!hasNext())
- return {};
- ++m_index;
- return std::pair<QString, AssetItem>(currentFileName(), at(m_index));
+ if (!empty() && m_index + 1 < size()) {
+ ++m_index;
+ return true;
+ }
+ return false;
}
private:
@@ -194,7 +159,7 @@ private:
};
QCache<QString, QSharedPointer<FolderIterator>> FolderIterator::m_assetsCache(std::max(50, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE")));
-QMutex FolderIterator::m_assetsCacheMutex;
+Q_CONSTINIT QMutex FolderIterator::m_assetsCacheMutex;
class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator
{
@@ -202,7 +167,7 @@ public:
AndroidAbstractFileEngineIterator(QDir::Filters filters,
const QStringList &nameFilters,
const QString &path)
- : QAbstractFileEngineIterator(filters, nameFilters)
+ : QAbstractFileEngineIterator(path, filters, nameFilters)
{
m_currentIterator = FolderIterator::fromCache(cleanedAssetPath(path), true);
}
@@ -219,28 +184,16 @@ public:
return m_currentIterator->currentFileName();
}
- virtual QString currentFilePath() const
+ QString currentFilePath() const override
{
if (!m_currentIterator)
return {};
return m_currentIterator->currentFilePath();
}
- bool hasNext() const override
- {
- if (!m_currentIterator)
- return false;
- return m_currentIterator->hasNext();
- }
-
- QString next() override
+ bool advance() override
{
- if (!m_currentIterator)
- return {};
- auto res = m_currentIterator->next();
- if (!res)
- return {};
- return res->first;
+ return m_currentIterator ? m_currentIterator->advance() : false;
}
private:
@@ -261,9 +214,11 @@ public:
close();
}
- bool open(QIODevice::OpenMode openMode) override
+ bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override
{
- if (m_isFolder || (openMode & QIODevice::WriteOnly))
+ Q_UNUSED(permissions);
+
+ if (!m_assetInfo || m_assetInfo->type != AssetItem::Type::File || (openMode & QIODevice::WriteOnly))
return false;
close();
m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
@@ -277,14 +232,13 @@ public:
m_assetFile = 0;
return true;
}
- m_isFolder = false;
return false;
}
qint64 size() const override
{
- if (m_assetFile)
- return AAsset_getLength(m_assetFile);
+ if (m_assetInfo)
+ return m_assetInfo->size;
return -1;
}
@@ -309,49 +263,41 @@ public:
return -1;
}
- bool isSequential() const override
- {
- return false;
- }
-
bool caseSensitive() const override
{
return true;
}
- bool isRelativePath() const override
- {
- return false;
- }
-
FileFlags fileFlags(FileFlags type = FileInfoAll) const override
{
FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
FileFlags flags;
- if (m_assetFile)
- flags = FileType | commonFlags;
- else if (m_isFolder)
- flags = DirectoryType | commonFlags;
+ if (m_assetInfo) {
+ if (m_assetInfo->type == AssetItem::Type::File)
+ flags = FileType | commonFlags;
+ else if (m_assetInfo->type == AssetItem::Type::Folder)
+ flags = DirectoryType | commonFlags;
+ }
return type & flags;
}
QString fileName(FileName file = DefaultName) const override
{
- int pos;
+ qsizetype pos;
switch (file) {
case DefaultName:
case AbsoluteName:
case CanonicalName:
return prefixedPath(m_fileName);
case BaseName:
- if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
- return prefixedPath(m_fileName.mid(pos));
+ if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
+ return m_fileName.mid(pos + 1);
else
- return prefixedPath(m_fileName);
+ return m_fileName;
case PathName:
case AbsolutePathName:
case CanonicalPathName:
- if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
+ if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
return prefixedPath(m_fileName.left(pos));
else
return prefixedPath(m_fileName);
@@ -366,22 +312,51 @@ public:
return;
close();
m_fileName = cleanedAssetPath(file);
- switch (FolderIterator::fileType(m_fileName)) {
- case AssetItem::Type::File:
- open(QIODevice::ReadOnly);
- break;
- case AssetItem::Type::Folder:
- m_isFolder = true;
- break;
- case AssetItem::Type::Invalid:
- break;
+
+ {
+ QMutexLocker lock(&m_assetsInfoCacheMutex);
+ QSharedPointer<AssetItem> *assetInfoPtr = m_assetsInfoCache.object(m_fileName);
+ if (assetInfoPtr) {
+ m_assetInfo = *assetInfoPtr;
+ return;
+ }
}
+
+ QSharedPointer<AssetItem> *newAssetInfoPtr = new QSharedPointer<AssetItem>(new AssetItem);
+
+ m_assetInfo = *newAssetInfoPtr;
+ m_assetInfo->name = m_fileName;
+ m_assetInfo->type = AssetItem::Type::Invalid;
+
+ m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
+
+ if (m_assetFile) {
+ m_assetInfo->type = AssetItem::Type::File;
+ m_assetInfo->size = AAsset_getLength(m_assetFile);
+ } else {
+ auto *assetDir = AAssetManager_openDir(m_assetManager, m_fileName.toUtf8());
+ if (assetDir) {
+ if (AAssetDir_getNextFileName(assetDir)
+ || (!FolderIterator::fromCache(m_fileName, false)->empty())) {
+ // If AAssetDir_getNextFileName is not valid, it still can be a directory that
+ // contains only other directories (no files). FolderIterator will not be called
+ // on the directory containing files so it should not be too time consuming now.
+ m_assetInfo->type = AssetItem::Type::Folder;
+ }
+ AAssetDir_close(assetDir);
+ }
+ }
+
+ QMutexLocker lock(&m_assetsInfoCacheMutex);
+ m_assetsInfoCache.insert(m_fileName, newAssetInfoPtr);
}
- Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
+ IteratorUniquePtr
+ beginEntryList(const QString &, QDir::Filters filters, const QStringList &filterNames) override
{
- if (m_isFolder)
- return new AndroidAbstractFileEngineIterator(filters, filterNames, m_fileName);
+ // AndroidAbstractFileEngineIterator use `m_fileName` as the path
+ if (m_assetInfo && m_assetInfo->type == AssetItem::Type::Folder)
+ return std::make_unique<AndroidAbstractFileEngineIterator>(filters, filterNames, m_fileName);
return nullptr;
}
@@ -389,31 +364,37 @@ private:
AAsset *m_assetFile = nullptr;
AAssetManager *m_assetManager = nullptr;
// initialize with a name that can't be used as a file name
- QString m_fileName = QLatin1String(".");
- bool m_isFolder = false;
+ QString m_fileName = "."_L1;
+ QSharedPointer<AssetItem> m_assetInfo;
+
+ static QCache<QString, QSharedPointer<AssetItem>> m_assetsInfoCache;
+ static QMutex m_assetsInfoCacheMutex;
};
+QCache<QString, QSharedPointer<AssetItem>> AndroidAbstractFileEngine::m_assetsInfoCache(std::max(200, qEnvironmentVariableIntValue("QT_ANDROID_MAX_FILEINFO_ASSETS_CACHE_SIZE")));
+Q_CONSTINIT QMutex AndroidAbstractFileEngine::m_assetsInfoCacheMutex;
AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler()
{
m_assetManager = QtAndroid::assetManager();
}
-QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &fileName) const
+std::unique_ptr<QAbstractFileEngine>
+AndroidAssetsFileEngineHandler::create(const QString &fileName) const
{
if (fileName.isEmpty())
- return nullptr;
+ return {};
if (!fileName.startsWith(assetsPrefix))
- return nullptr;
+ return {};
QString path = fileName.mid(prefixSize);
- path.replace(QLatin1String("//"), QLatin1String("/"));
- if (path.startsWith(QLatin1Char('/')))
+ path.replace("//"_L1, "/"_L1);
+ if (path.startsWith(u'/'))
path.remove(0, 1);
- if (path.endsWith(QLatin1Char('/')))
+ if (path.endsWith(u'/'))
path.chop(1);
- return new AndroidAbstractFileEngine(m_assetManager, path);
+ return std::make_unique<AndroidAbstractFileEngine>(m_assetManager, path);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.h b/src/plugins/platforms/android/qandroidassetsfileenginehandler.h
index 51cc5b07a8..973a61fbfa 100644
--- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.h
+++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDASSETSFILEENGINEHANDLER_H
#define QANDROIDASSETSFILEENGINEHANDLER_H
@@ -51,9 +15,10 @@ QT_BEGIN_NAMESPACE
class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler
{
+ Q_DISABLE_COPY_MOVE(AndroidAssetsFileEngineHandler)
public:
AndroidAssetsFileEngineHandler();
- QAbstractFileEngine *create(const QString &fileName) const override;
+ std::unique_ptr<QAbstractFileEngine> create(const QString &fileName) const override;
private:
AAssetManager *m_assetManager;
diff --git a/src/plugins/platforms/android/qandroideventdispatcher.cpp b/src/plugins/platforms/android/qandroideventdispatcher.cpp
index 3a1fb7a6de..8d1a085844 100644
--- a/src/plugins/platforms/android/qandroideventdispatcher.cpp
+++ b/src/plugins/platforms/android/qandroideventdispatcher.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroideventdispatcher.h"
#include "androidjnimain.h"
@@ -101,7 +65,7 @@ bool QAndroidEventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags
QAndroidEventDispatcherStopper *QAndroidEventDispatcherStopper::instance()
{
- static QAndroidEventDispatcherStopper androidEventDispatcherStopper;
+ Q_CONSTINIT static QAndroidEventDispatcherStopper androidEventDispatcherStopper;
return &androidEventDispatcherStopper;
}
@@ -111,7 +75,7 @@ void QAndroidEventDispatcherStopper::startAll()
if (!m_started.testAndSetOrdered(0, 1))
return;
- for (QAndroidEventDispatcher *d : qAsConst(m_dispatchers))
+ for (QAndroidEventDispatcher *d : std::as_const(m_dispatchers))
d->start();
}
@@ -121,7 +85,7 @@ void QAndroidEventDispatcherStopper::stopAll()
if (!m_started.testAndSetOrdered(1, 0))
return;
- for (QAndroidEventDispatcher *d : qAsConst(m_dispatchers))
+ for (QAndroidEventDispatcher *d : std::as_const(m_dispatchers))
d->stop();
}
@@ -140,6 +104,6 @@ void QAndroidEventDispatcherStopper::removeEventDispatcher(QAndroidEventDispatch
void QAndroidEventDispatcherStopper::goingToStop(bool stop)
{
QMutexLocker lock(&m_mutex);
- for (QAndroidEventDispatcher *d : qAsConst(m_dispatchers))
+ for (QAndroidEventDispatcher *d : std::as_const(m_dispatchers))
d->goingToStop(stop);
}
diff --git a/src/plugins/platforms/android/qandroideventdispatcher.h b/src/plugins/platforms/android/qandroideventdispatcher.h
index 8a29ed7e7e..a086b705b1 100644
--- a/src/plugins/platforms/android/qandroideventdispatcher.h
+++ b/src/plugins/platforms/android/qandroideventdispatcher.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDEVENTDISPATCHER_H
#define QANDROIDEVENTDISPATCHER_H
diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp
index fb51e1dba1..62212ff63d 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.cpp
+++ b/src/plugins/platforms/android/qandroidinputcontext.cpp
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <android/log.h>
@@ -60,6 +24,7 @@
#include <qthread.h>
#include <qwindow.h>
#include <qpa/qplatformwindow.h>
+
QT_BEGIN_NAMESPACE
namespace {
@@ -115,9 +80,7 @@ static jboolean beginBatchEdit(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ BEGINBATCH");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ BEGINBATCH";
jboolean res = JNI_FALSE;
runOnQtThread([&res]{res = m_androidInputContext->beginBatchEdit();});
return res;
@@ -128,9 +91,7 @@ static jboolean endBatchEdit(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ ENDBATCH");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ ENDBATCH";
jboolean res = JNI_FALSE;
runOnQtThread([&res]{res = m_androidInputContext->endBatchEdit();});
@@ -148,9 +109,7 @@ static jboolean commitText(JNIEnv *env, jobject /*thiz*/, jstring text, jint new
QString str(reinterpret_cast<const QChar *>(jstr), env->GetStringLength(text));
env->ReleaseStringChars(text, jstr);
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ COMMIT" << str << newCursorPosition;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ COMMIT" << str << newCursorPosition;
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->commitText(str, newCursorPosition);});
return res;
@@ -161,9 +120,7 @@ static jboolean deleteSurroundingText(JNIEnv */*env*/, jobject /*thiz*/, jint le
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ DELETE" << leftLength << rightLength;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ DELETE" << leftLength << rightLength;
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->deleteSurroundingText(leftLength, rightLength);});
return res;
@@ -174,9 +131,7 @@ static jboolean finishComposingText(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ FINISH");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ FINISH";
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->finishComposingText();});
return res;
@@ -200,9 +155,7 @@ static jobject getExtractedText(JNIEnv *env, jobject /*thiz*/, int hintMaxChars,
QAndroidInputContext::ExtractedText extractedText;
runOnQtThread([&]{extractedText = m_androidInputContext->getExtractedText(hintMaxChars, hintMaxLines, flags);});
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ GETEX" << hintMaxChars << hintMaxLines << QString::fromLatin1("0x") + QString::number(flags,16) << extractedText.text << "partOff:" << extractedText.partialStartOffset << extractedText.partialEndOffset << "sel:" << extractedText.selectionStart << extractedText.selectionEnd << "offset:" << extractedText.startOffset;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ GETEX" << hintMaxChars << hintMaxLines << QString::fromLatin1("0x") + QString::number(flags,16) << extractedText.text << "partOff:" << extractedText.partialStartOffset << extractedText.partialEndOffset << "sel:" << extractedText.selectionStart << extractedText.selectionEnd << "offset:" << extractedText.startOffset;
jobject object = env->NewObject(m_extractedTextClass, m_classConstructorMethodID);
env->SetIntField(object, m_partialStartOffsetFieldID, extractedText.partialStartOffset);
@@ -225,9 +178,7 @@ static jstring getSelectedText(JNIEnv *env, jobject /*thiz*/, jint flags)
QString text;
runOnQtThread([&]{text = m_androidInputContext->getSelectedText(flags);});
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ GETSEL" << text;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ GETSEL" << text;
if (text.isEmpty())
return 0;
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
@@ -240,9 +191,7 @@ static jstring getTextAfterCursor(JNIEnv *env, jobject /*thiz*/, jint length, ji
QString text;
runOnQtThread([&]{text = m_androidInputContext->getTextAfterCursor(length, flags);});
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ GETA" << length << text;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ GETA" << length << text;
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -253,9 +202,7 @@ static jstring getTextBeforeCursor(JNIEnv *env, jobject /*thiz*/, jint length, j
QString text;
runOnQtThread([&]{text = m_androidInputContext->getTextBeforeCursor(length, flags);});
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ GETB" << length << text;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ GETB" << length << text;
return env->NewString(reinterpret_cast<const jchar *>(text.constData()), jsize(text.length()));
}
@@ -269,9 +216,7 @@ static jboolean setComposingText(JNIEnv *env, jobject /*thiz*/, jstring text, ji
QString str(reinterpret_cast<const QChar *>(jstr), env->GetStringLength(text));
env->ReleaseStringChars(text, jstr);
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ SET" << str << newCursorPosition;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ SET" << str << newCursorPosition;
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->setComposingText(str, newCursorPosition);});
return res;
@@ -282,9 +227,7 @@ static jboolean setComposingRegion(JNIEnv */*env*/, jobject /*thiz*/, jint start
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ SETR" << start << end;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ SETR" << start << end;
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->setComposingRegion(start, end);});
return res;
@@ -296,9 +239,7 @@ static jboolean setSelection(JNIEnv */*env*/, jobject /*thiz*/, jint start, jint
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug() << "@@@ SETSEL" << start << end;
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ SETSEL" << start << end;
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->setSelection(start, end);});
return res;
@@ -310,9 +251,7 @@ static jboolean selectAll(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ SELALL");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ SELALL";
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->selectAll();});
return res;
@@ -323,9 +262,7 @@ static jboolean cut(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@";
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->cut();});
return res;
@@ -336,9 +273,7 @@ static jboolean copy(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@";
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->copy();});
return res;
@@ -349,9 +284,7 @@ static jboolean copyURL(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@";
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->copyURL();});
return res;
@@ -362,9 +295,7 @@ static jboolean paste(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ PASTE");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ PASTE";
jboolean res = JNI_FALSE;
runOnQtThread([&]{res = m_androidInputContext->paste();});
return res;
@@ -375,14 +306,24 @@ static jboolean updateCursorPosition(JNIEnv */*env*/, jobject /*thiz*/)
if (!m_androidInputContext)
return JNI_FALSE;
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qDebug("@@@ UPDATECURSORPOS");
-#endif
+ qCDebug(lcQpaInputMethods) << "@@@ UPDATECURSORPOS";
runOnQtThread([&]{m_androidInputContext->updateCursorPosition();});
return true;
}
+static void reportFullscreenMode(JNIEnv */*env*/, jobject /*thiz*/, jboolean enabled)
+{
+ if (!m_androidInputContext)
+ return;
+
+ runOnQtThread([&]{m_androidInputContext->reportFullscreenMode(enabled);});
+}
+
+static jboolean fullscreenMode(JNIEnv */*env*/, jobject /*thiz*/)
+{
+ return m_androidInputContext ? m_androidInputContext->fullscreenMode() : false;
+}
static JNINativeMethod methods[] = {
{"beginBatchEdit", "()Z", (void *)beginBatchEdit},
@@ -403,7 +344,9 @@ static JNINativeMethod methods[] = {
{"copy", "()Z", (void *)copy},
{"copyURL", "()Z", (void *)copyURL},
{"paste", "()Z", (void *)paste},
- {"updateCursorPosition", "()Z", (void *)updateCursorPosition}
+ {"updateCursorPosition", "()Z", (void *)updateCursorPosition},
+ {"reportFullscreenMode", "(Z)V", (void *)reportFullscreenMode},
+ {"fullscreenMode", "()Z", (void *)fullscreenMode}
};
static QRect screenInputItemRectangle()
@@ -420,6 +363,7 @@ QAndroidInputContext::QAndroidInputContext()
, m_handleMode(Hidden)
, m_batchEditNestingLevel(0)
, m_focusObject(0)
+ , m_fullScreenMode(false)
{
QJniEnvironment env;
jclass clazz = env.findClass(QtNativeInputConnectionClassName);
@@ -492,7 +436,7 @@ QAndroidInputContext::QAndroidInputContext()
m_androidInputContext = this;
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::cursorRectangleChanged,
- this, &QAndroidInputContext::updateInputItemRectangle);
+ this, &QAndroidInputContext::updateSelectionHandles);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::anchorRectangleChanged,
this, &QAndroidInputContext::updateSelectionHandles);
QObject::connect(QGuiApplication::inputMethod(), &QInputMethod::inputItemClipRectangleChanged, this, [this]{
@@ -602,12 +546,29 @@ void QAndroidInputContext::updateCursorPosition()
}
}
+bool QAndroidInputContext::isImhNoTextHandlesSet()
+{
+ QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
+ if (query.isNull())
+ return false;
+ return query->value(Qt::ImHints).toUInt() & Qt::ImhNoTextHandles;
+}
+
void QAndroidInputContext::updateSelectionHandles()
{
+ if (m_fullScreenMode) {
+ QtAndroidInput::updateHandles(Hidden);
+ return;
+ }
static bool noHandles = qEnvironmentVariableIntValue("QT_QPA_NO_TEXT_HANDLES");
if (noHandles || !m_focusObject)
return;
+ if (isImhNoTextHandlesSet()) {
+ QtAndroidInput::updateHandles(Hidden);
+ return;
+ }
+
auto im = qGuiApp->inputMethod();
QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImAnchorPosition | Qt::ImEnabled
@@ -621,6 +582,11 @@ void QAndroidInputContext::updateSelectionHandles()
bool readOnly = readOnlyVariant.toBool();
QPlatformWindow *qPlatformWindow = qGuiApp->focusWindow()->handle();
+ if (!readOnly && ((m_handleMode & 0xff) == Hidden)) {
+ QtAndroidInput::updateHandles(Hidden);
+ return;
+ }
+
if ( cpos == anchor && (!readOnlyVariant.isValid() || readOnly)) {
QtAndroidInput::updateHandles(Hidden);
return;
@@ -628,17 +594,27 @@ void QAndroidInputContext::updateSelectionHandles()
if (cpos == anchor || im->anchorRectangle().isNull()) {
auto curRect = cursorRectangle();
- QPoint cursorPoint = qPlatformWindow->mapToGlobal(QPoint(curRect.x() + (curRect.width() / 2), curRect.y() + curRect.height()));
- QPoint editMenuPoint(cursorPoint.x(), cursorPoint.y());
+ QPoint cursorPointGlobal = qPlatformWindow->mapToGlobal(
+ QPoint(curRect.x() + (curRect.width() / 2), curRect.y() + curRect.height()));
+ QPoint cursorPoint(curRect.center().x(), curRect.bottom());
+ int x = curRect.x();
+ int y = curRect.y();
+
+ // Use x and y for the editMenuPoint from the cursorPointGlobal when the cursor is in the Dialog
+ if (cursorPointGlobal != cursorPoint) {
+ x = cursorPointGlobal.x();
+ y = cursorPointGlobal.y();
+ }
+
+ QPoint editMenuPoint(x, y);
m_handleMode &= ShowEditPopup;
m_handleMode |= ShowCursor;
uint32_t buttons = readOnly ? 0 : EditContext::PasteButton;
if (!query.value(Qt::ImSurroundingText).toString().isEmpty())
buttons |= EditContext::SelectAllButton;
- QtAndroidInput::updateHandles(m_handleMode, editMenuPoint, buttons, cursorPoint);
- // The VK is hidden, reset the timer
- if (m_hideCursorHandleTimer.isActive())
- m_hideCursorHandleTimer.start();
+ QtAndroidInput::updateHandles(m_handleMode, editMenuPoint, buttons, cursorPointGlobal);
+ m_hideCursorHandleTimer.start();
+
return;
}
@@ -653,22 +629,26 @@ void QAndroidInputContext::updateSelectionHandles()
QPoint leftPoint(qPlatformWindow->mapToGlobal(leftRect.bottomLeft().toPoint()));
QPoint rightPoint(qPlatformWindow->mapToGlobal(rightRect.bottomRight().toPoint()));
- if (m_selectHandleWidth == 0)
- m_selectHandleWidth = QtAndroidInput::getSelectHandleWidth() / 2;
- int rightSideOfScreen = QtAndroid::androidPlatformIntegration()->screen()->availableGeometry().right();
- if (leftPoint.x() < m_selectHandleWidth)
- leftPoint.setX(m_selectHandleWidth);
+ QAndroidPlatformIntegration *platformIntegration = QtAndroid::androidPlatformIntegration();
+ if (platformIntegration) {
+ if (m_selectHandleWidth == 0)
+ m_selectHandleWidth = QtAndroidInput::getSelectHandleWidth() / 2;
- if (rightPoint.x() > rightSideOfScreen - m_selectHandleWidth)
- rightPoint.setX(rightSideOfScreen - m_selectHandleWidth);
+ int rightSideOfScreen = platformIntegration->screen()->availableGeometry().right();
+ if (leftPoint.x() < m_selectHandleWidth)
+ leftPoint.setX(m_selectHandleWidth);
- QPoint editPoint(qPlatformWindow->mapToGlobal(leftRect.united(rightRect).topLeft().toPoint()));
- uint32_t buttons = readOnly ? EditContext::CopyButton | EditContext::SelectAllButton
- : EditContext::AllButtons;
+ if (rightPoint.x() > rightSideOfScreen - m_selectHandleWidth)
+ rightPoint.setX(rightSideOfScreen - m_selectHandleWidth);
- QtAndroidInput::updateHandles(m_handleMode, editPoint, buttons, leftPoint, rightPoint,
- query.value(Qt::ImCurrentSelection).toString().isRightToLeft());
- m_hideCursorHandleTimer.stop();
+ QPoint editPoint(qPlatformWindow->mapToGlobal(leftRect.united(rightRect).topLeft().toPoint()));
+ uint32_t buttons = readOnly ? EditContext::CopyButton | EditContext::SelectAllButton
+ : EditContext::AllButtons;
+
+ QtAndroidInput::updateHandles(m_handleMode, editPoint, buttons, leftPoint, rightPoint,
+ query.value(Qt::ImCurrentSelection).toString().isRightToLeft());
+ m_hideCursorHandleTimer.stop();
+ }
}
/*
@@ -808,7 +788,15 @@ void QAndroidInputContext::touchDown(int x, int y)
focusObjectStopComposing();
}
- updateSelectionHandles();
+ // Check if cursor is visible in focused window before updating handles
+ QPlatformWindow *window = qGuiApp->focusWindow()->handle();
+ const QRectF curRect = cursorRectangle();
+ const QPoint cursorGlobalPoint = window->mapToGlobal(QPoint(curRect.x(), curRect.y()));
+ const QRect windowRect = QPlatformInputContext::inputItemClipRectangle().toRect();
+ const QRect windowGlobalRect = QRect(window->mapToGlobal(windowRect.topLeft()), windowRect.size());
+
+ if (windowGlobalRect.contains(cursorGlobalPoint.x(), cursorGlobalPoint.y()))
+ updateSelectionHandles();
}
}
@@ -882,44 +870,9 @@ void QAndroidInputContext::update(Qt::InputMethodQueries queries)
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery(queries);
if (query.isNull())
return;
-
- if (query->value(Qt::ImCursorPosition).toInt() >= 0 &&
- query->value(Qt::ImSurroundingText).toString()
- .left(query->value(Qt::ImCursorPosition).toInt()).length() >= 0) {
-
- // Cursor position should be always valid
- // when object is composing
- if (focusObjectIsComposing())
- return;
-
- // NOTE: This seems to be happening sometimes
- // when qt quick application is booted up
- if (m_focusObject == nullptr)
- return;
-
- if (m_focusObject->isWidgetType())
- updateCursorPosition();
- else
- updateCursorPositionInRange(query);
- }
+#warning TODO extract the needed data from query
}
-void QAndroidInputContext::updateCursorPositionInRange(const QSharedPointer<QInputMethodQueryEvent> &query)
-{
- QObject *input = qGuiApp->focusObject();
- QList<QInputMethodEvent::Attribute> attributes;
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
- query->value(Qt::ImCursorPosition).toInt(), 1));
-
- QInputMethodEvent event(QString(), attributes);
- QCoreApplication::sendEvent(input, &event);
-
- QtAndroidInput::updateSelection(query->value(Qt::ImCursorPosition).toInt(),
- query->value(Qt::ImCursorPosition).toInt(), 0,
- query->value(Qt::ImSurroundingText).toString().length());
-}
-
-
void QAndroidInputContext::invokeAction(QInputMethod::Action action, int cursorPosition)
{
#warning TODO Handle at least QInputMethod::ContextMenu action
@@ -950,49 +903,21 @@ void QAndroidInputContext::showInputPanel()
if (query.isNull())
return;
- QRect rect = cursorRect();
- if (!isInputPanelVisible())
- QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
- screenInputItemRectangle().height(),
- query->value(Qt::ImHints).toUInt(),
- query->value(Qt::ImEnterKeyType).toUInt());
-}
-
-QRect QAndroidInputContext::cursorRect()
-{
- QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
- // if single line, we do not want to mess with the editor's position, as we do not
- // have to follow the cursor in vertical axis
- if (query.isNull()
- || (query->value(Qt::ImHints).toUInt() & Qt::ImhMultiLine) != Qt::ImhMultiLine)
- return {};
+ if (!qGuiApp->focusWindow()->handle())
+ return; // not a real window, probably VR/XR
- auto im = qGuiApp->inputMethod();
- if (!im)
- return {};
+ disconnect(m_updateCursorPosConnection);
+ m_updateCursorPosConnection = {};
- const auto cursorRect= im->cursorRectangle().toRect();
- QRect finalRect(inputItemRectangle().toRect());
- const QWindow *window = qGuiApp->focusWindow();
- const double pd = window
- ? QHighDpiScaling::factor(window)
- : QHighDpiScaling::factor(QtAndroid::androidPlatformIntegration()->screen());
- finalRect.setY(cursorRect.y() * pd);
- finalRect.setHeight(cursorRect.height() * pd);
- //fiddle a bit with vert margins, so the tracking rectangle is not too tight.
- finalRect += QMargins(0, cursorRect.height() / 4, 0, cursorRect.height() / 4);
- return finalRect;
-}
+ if (qGuiApp->focusObject()->metaObject()->indexOfSignal("cursorPositionChanged(int,int)") >= 0) // QLineEdit breaks the pattern
+ m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged(int,int)), this, SLOT(updateCursorPosition()));
+ else if (qGuiApp->focusObject()->metaObject()->indexOfSignal("cursorPositionChanged()") >= 0)
+ m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
-void QAndroidInputContext::updateInputItemRectangle()
-{
- QRect rect = cursorRect();
-
- if (!rect.isValid())
- return;
- QtAndroidInput::updateInputItemRectangle(rect.left(), rect.top(),
- rect.width(), rect.height());
- updateSelectionHandles();
+ QRect rect = screenInputItemRectangle();
+ QtAndroidInput::showSoftwareKeyboard(rect.left(), rect.top(), rect.width(), rect.height(),
+ query->value(Qt::ImHints).toUInt(),
+ query->value(Qt::ImEnterKeyType).toUInt());
}
void QAndroidInputContext::showInputPanelLater(Qt::ApplicationState state)
@@ -1042,7 +967,6 @@ void QAndroidInputContext::setFocusObject(QObject *object)
m_focusObject = object;
reset();
}
- QPlatformInputContext::setFocusObject(object);
updateSelectionHandles();
}
@@ -1096,7 +1020,7 @@ jboolean QAndroidInputContext::deleteSurroundingText(jint leftLength, jint right
absolutely not what Android's native EditText does. It deletes leftLength characters before
min(selection start, composing region start) and rightLength characters after max(selection
end, composing region end). There are no known keyboards that depend on this behavior, but
- it is better to be consistent with EditText behavior, because there definetly should be no
+ it is better to be consistent with EditText behavior, because there definitely should be no
keyboards that depend on documented behavior.
*/
const int leftEnd =
@@ -1199,6 +1123,25 @@ jboolean QAndroidInputContext::finishComposingText()
return JNI_TRUE;
}
+void QAndroidInputContext::reportFullscreenMode(jboolean enabled)
+{
+ m_fullScreenMode = enabled;
+ BatchEditLock batchEditLock(this);
+ if (!focusObjectStopComposing())
+ return;
+
+ if (enabled)
+ m_handleMode = Hidden;
+
+ updateSelectionHandles();
+}
+
+// Called in calling thread's context
+jboolean QAndroidInputContext::fullscreenMode()
+{
+ return m_fullScreenMode;
+}
+
bool QAndroidInputContext::focusObjectIsComposing() const
{
return m_composingCursor != -1;
@@ -1210,7 +1153,7 @@ void QAndroidInputContext::focusObjectStartComposing()
return;
// Composing strings containing newline characters are rare and may cause problems
- if (m_composingText.contains(QLatin1Char('\n')))
+ if (m_composingText.contains(u'\n'))
return;
QSharedPointer<QInputMethodQueryEvent> query = focusObjectInputMethodQuery();
@@ -1254,13 +1197,21 @@ bool QAndroidInputContext::focusObjectStopComposing()
m_composingCursor = -1;
- // Moving Qt's cursor to where the preedit cursor used to be
- QList<QInputMethodEvent::Attribute> attributes;
- attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
-
- QInputMethodEvent event(QString(), attributes);
- event.setCommitString(m_composingText);
- sendInputMethodEvent(&event);
+ {
+ // commit the composing test
+ QList<QInputMethodEvent::Attribute> attributes;
+ QInputMethodEvent event(QString(), attributes);
+ event.setCommitString(m_composingText);
+ sendInputMethodEvent(&event);
+ }
+ {
+ // Moving Qt's cursor to where the preedit cursor used to be
+ QList<QInputMethodEvent::Attribute> attributes;
+ attributes.append(
+ QInputMethodEvent::Attribute(QInputMethodEvent::Selection, localCursorPos, 0));
+ QInputMethodEvent event(QString(), attributes);
+ sendInputMethodEvent(&event);
+ }
return true;
}
@@ -1285,7 +1236,7 @@ jint QAndroidInputContext::getCursorCapsMode(jint /*reqModes*/)
if (focusObjectIsComposing())
surroundingText += QStringView{m_composingText}.left(m_composingCursor - m_composingTextStart);
// Add a character to see if it is at the end of the sentence or not
- QTextBoundaryFinder finder(QTextBoundaryFinder::Sentence, surroundingText + QLatin1Char('A'));
+ QTextBoundaryFinder finder(QTextBoundaryFinder::Sentence, surroundingText + u'A');
finder.setPosition(surroundingText.length());
if (finder.isAtBoundary())
atWordBoundary = finder.isAtBoundary();
@@ -1505,7 +1456,7 @@ jboolean QAndroidInputContext::setComposingText(const QString &text, jint newCur
const bool focusObjectWasComposing = focusObjectIsComposing();
// Same checks as in focusObjectStartComposing()
- if (!m_composingText.isEmpty() && !m_composingText.contains(QLatin1Char('\n'))
+ if (!m_composingText.isEmpty() && !m_composingText.contains(u'\n')
&& newAbsoluteCursorPos >= m_composingTextStart
&& newAbsoluteCursorPos <= m_composingTextStart + m_composingText.length())
m_composingCursor = newAbsoluteCursorPos;
@@ -1613,9 +1564,7 @@ jboolean QAndroidInputContext::setComposingRegion(jint start, jint end)
}
if (start < textOffset || end - textOffset > text.length()) {
-#ifdef QT_DEBUG_ANDROID_IM_PROTOCOL
- qWarning("setComposingRegion: failed to retrieve text from composing region");
-#endif
+ qCDebug(lcQpaInputMethods) << "Warning: setComposingRegion: failed to retrieve text from composing region";
return JNI_TRUE;
}
diff --git a/src/plugins/platforms/android/qandroidinputcontext.h b/src/plugins/platforms/android/qandroidinputcontext.h
index 521061b02c..038286c4b8 100644
--- a/src/plugins/platforms/android/qandroidinputcontext.h
+++ b/src/plugins/platforms/android/qandroidinputcontext.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDINPUTCONTEXT_H
#define ANDROIDINPUTCONTEXT_H
@@ -45,6 +9,8 @@
#include <functional>
#include <jni.h>
#include <qevent.h>
+
+#include <QtCore/qpointer.h>
#include <QTimer>
QT_BEGIN_NAMESPACE
@@ -134,11 +100,12 @@ public:
jboolean copy();
jboolean copyURL();
jboolean paste();
+ void reportFullscreenMode(jboolean enabled);
+ jboolean fullscreenMode();
public slots:
void safeCall(const std::function<void()> &func, Qt::ConnectionType conType = Qt::BlockingQueuedConnection);
void updateCursorPosition();
- void updateInputItemRectangle();
void updateSelectionHandles();
void handleLocationChanged(int handleId, int x, int y);
void touchDown(int x, int y);
@@ -150,23 +117,24 @@ private slots:
void showInputPanelLater(Qt::ApplicationState);
private:
+ bool isImhNoTextHandlesSet();
void sendInputMethodEvent(QInputMethodEvent *event);
QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll);
bool focusObjectIsComposing() const;
void focusObjectStartComposing();
bool focusObjectStopComposing();
- QRect cursorRect();
- void updateCursorPositionInRange(const QSharedPointer<QInputMethodQueryEvent> &query);
private:
ExtractedText m_extractedText;
QString m_composingText;
int m_composingTextStart;
int m_composingCursor;
+ QMetaObject::Connection m_updateCursorPosConnection;
HandleModes m_handleMode;
int m_batchEditNestingLevel;
- QObject *m_focusObject;
+ QPointer<QObject> m_focusObject;
QTimer m_hideCursorHandleTimer;
+ bool m_fullScreenMode;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QAndroidInputContext::HandleModes)
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformaccessibility.cpp b/src/plugins/platforms/android/qandroidplatformaccessibility.cpp
index cc05dad749..ea7f22295d 100644
--- a/src/plugins/platforms/android/qandroidplatformaccessibility.cpp
+++ b/src/plugins/platforms/android/qandroidplatformaccessibility.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformaccessibility.h"
@@ -61,12 +25,24 @@ void QAndroidPlatformAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *
// so that the element can be moved on the screen if it's focused.
if (event->type() == QAccessible::LocationChanged) {
- QtAndroidAccessibility::notifyLocationChange();
+ QtAndroidAccessibility::notifyLocationChange(event->uniqueId());
} else if (event->type() == QAccessible::ObjectHide) {
QtAndroidAccessibility::notifyObjectHide(event->uniqueId());
+ } else if (event->type() == QAccessible::ObjectShow) {
+ QtAndroidAccessibility::notifyObjectShow(event->uniqueId());
} else if (event->type() == QAccessible::Focus) {
QtAndroidAccessibility::notifyObjectFocus(event->uniqueId());
+ } else if (event->type() == QAccessible::ValueChanged) {
+ QtAndroidAccessibility::notifyValueChanged(event->uniqueId());
+ } else if (event->type() == QAccessible::ScrollingEnd) {
+ QtAndroidAccessibility::notifyScrolledEvent(event->uniqueId());
}
}
+void QAndroidPlatformAccessibility::setRootObject(QObject *obj)
+{
+ QPlatformAccessibility::setRootObject(obj);
+ QtAndroidAccessibility::createAccessibilityContextObject(obj);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformaccessibility.h b/src/plugins/platforms/android/qandroidplatformaccessibility.h
index 8216c05fa6..9fcc45397a 100644
--- a/src/plugins/platforms/android/qandroidplatformaccessibility.h
+++ b/src/plugins/platforms/android/qandroidplatformaccessibility.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMACCESSIBILITY_H
@@ -52,6 +16,7 @@ public:
~QAndroidPlatformAccessibility();
void notifyAccessibilityUpdate(QAccessibleEvent *event) override;
+ void setRootObject(QObject *obj) override;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformbackingstore.cpp b/src/plugins/platforms/android/qandroidplatformbackingstore.cpp
deleted file mode 100644
index 61d4969c41..0000000000
--- a/src/plugins/platforms/android/qandroidplatformbackingstore.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qandroidplatformbackingstore.h"
-#include "qandroidplatformscreen.h"
-#include "qandroidplatformwindow.h"
-#include <qpa/qplatformscreen.h>
-
-QT_BEGIN_NAMESPACE
-
-QAndroidPlatformBackingStore::QAndroidPlatformBackingStore(QWindow *window)
- : QPlatformBackingStore(window)
-{
- if (window->handle())
- setBackingStore(window);
-}
-
-QPaintDevice *QAndroidPlatformBackingStore::paintDevice()
-{
- return &m_image;
-}
-
-void QAndroidPlatformBackingStore::flush(QWindow *window, const QRegion &region, const QPoint &offset)
-{
- Q_UNUSED(offset);
-
- if (!m_backingStoreSet)
- setBackingStore(window);
-
- (static_cast<QAndroidPlatformWindow *>(window->handle()))->repaint(region);
-}
-
-void QAndroidPlatformBackingStore::resize(const QSize &size, const QRegion &staticContents)
-{
- Q_UNUSED(staticContents);
-
- if (m_image.size() != size)
- m_image = QImage(size, window()->screen()->handle()->format());
-}
-
-void QAndroidPlatformBackingStore::setBackingStore(QWindow *window)
-{
- if (window->surfaceType() == QSurface::RasterSurface || window->surfaceType() == QSurface::RasterGLSurface) {
- (static_cast<QAndroidPlatformWindow *>(window->handle()))->setBackingStore(this);
- m_backingStoreSet = true;
- } else {
- qWarning("QAndroidPlatformBackingStore does not support OpenGL-only windows.");
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformbackingstore.h b/src/plugins/platforms/android/qandroidplatformbackingstore.h
deleted file mode 100644
index a3a65aa30e..0000000000
--- a/src/plugins/platforms/android/qandroidplatformbackingstore.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QANDROIDPLATFORMBACKINGSTORE_H
-#define QANDROIDPLATFORMBACKINGSTORE_H
-
-#include <qpa/qplatformbackingstore.h>
-#include <qpa/qwindowsysteminterface.h>
-
-QT_BEGIN_NAMESPACE
-
-class QAndroidPlatformBackingStore : public QPlatformBackingStore
-{
-public:
- explicit QAndroidPlatformBackingStore(QWindow *window);
- QPaintDevice *paintDevice() override;
- void flush(QWindow *window, const QRegion &region, const QPoint &offset) override;
- void resize(const QSize &size, const QRegion &staticContents) override;
- QImage toImage() const override { return m_image; }
- void setBackingStore(QWindow *window);
-protected:
- QImage m_image;
- bool m_backingStoreSet = false;
-};
-
-QT_END_NAMESPACE
-
-#endif // QANDROIDPLATFORMBACKINGSTORE_H
diff --git a/src/plugins/platforms/android/qandroidplatformclipboard.cpp b/src/plugins/platforms/android/qandroidplatformclipboard.cpp
index cc32bbf771..e5ed33b9b0 100644
--- a/src/plugins/platforms/android/qandroidplatformclipboard.cpp
+++ b/src/plugins/platforms/android/qandroidplatformclipboard.cpp
@@ -1,51 +1,34 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformclipboard.h"
-#include "androidjniclipboard.h"
+
+#include <QtCore/QUrl>
+#include <QtCore/QJniEnvironment>
+#include <QtCore/QJniObject>
+#include <QtCore/private/qjnihelpers_p.h>
+
#ifndef QT_NO_CLIPBOARD
+using namespace QtJniTypes;
+
QT_BEGIN_NAMESPACE
+void QAndroidPlatformClipboard::onClipboardDataChanged(JNIEnv *env, jobject obj, jlong nativePointer)
+{
+ Q_UNUSED(env)
+ Q_UNUSED(obj)
+
+ auto *clipboardManager = reinterpret_cast<QAndroidPlatformClipboard *>(nativePointer);
+ if (clipboardManager)
+ clipboardManager->emitChanged(QClipboard::Clipboard);
+}
+
QAndroidPlatformClipboard::QAndroidPlatformClipboard()
{
- QtAndroidClipboard::setClipboardManager(this);
+ m_clipboardManager = QtClipboardManager::construct(QtAndroidPrivate::context(),
+ reinterpret_cast<jlong>(this));
}
QAndroidPlatformClipboard::~QAndroidPlatformClipboard()
@@ -54,24 +37,66 @@ QAndroidPlatformClipboard::~QAndroidPlatformClipboard()
delete data;
}
+QMimeData *QAndroidPlatformClipboard::getClipboardMimeData()
+{
+ QMimeData *data = new QMimeData;
+ if (m_clipboardManager.callMethod<jboolean>("hasClipboardText")) {
+ data->setText(m_clipboardManager.callMethod<QString>("getClipboardText"));
+ }
+ if (m_clipboardManager.callMethod<jboolean>("hasClipboardHtml")) {
+ data->setHtml(m_clipboardManager.callMethod<QString>("getClipboardHtml"));
+ }
+ if (m_clipboardManager.callMethod<jboolean>("hasClipboardUri")) {
+ auto uris = m_clipboardManager.callMethod<QString[]>("getClipboardUris");
+ if (uris.isValid()) {
+ QList<QUrl> urls;
+ for (const QString &uri : uris)
+ urls << QUrl(uri);
+ data->setUrls(urls);
+ }
+ }
+ return data;
+}
+
QMimeData *QAndroidPlatformClipboard::mimeData(QClipboard::Mode mode)
{
Q_UNUSED(mode);
Q_ASSERT(supportsMode(mode));
if (data)
data->deleteLater();
- data = QtAndroidClipboard::getClipboardMimeData();
+ data = getClipboardMimeData();
return data;
}
+void QAndroidPlatformClipboard::clearClipboardData()
+{
+ m_clipboardManager.callMethod<void>("clearClipData");
+}
+
+void QAndroidPlatformClipboard::setClipboardMimeData(QMimeData *data)
+{
+ clearClipboardData();
+ auto context = QtAndroidPrivate::context();
+ if (data->hasUrls()) {
+ QList<QUrl> urls = data->urls();
+ for (const auto &u : std::as_const(urls))
+ m_clipboardManager.callMethod<void>("setClipboardUri", context, u.toEncoded());
+ } else if (data->hasHtml()) { // html can contain text
+ m_clipboardManager.callMethod<void>("setClipboardHtml",
+ context, data->text(), data->html());
+ } else if (data->hasText()) { // hasText must be the last (the order matter here)
+ m_clipboardManager.callMethod<void>("setClipboardText", context, data->text());
+ }
+}
+
void QAndroidPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
{
if (!data) {
- QtAndroidClipboard::clearClipboardData();
+ clearClipboardData();
return;
}
if (data && supportsMode(mode))
- QtAndroidClipboard::setClipboardMimeData(data);
+ setClipboardMimeData(data);
if (data != 0)
data->deleteLater();
}
@@ -81,6 +106,18 @@ bool QAndroidPlatformClipboard::supportsMode(QClipboard::Mode mode) const
return QClipboard::Clipboard == mode;
}
+bool QAndroidPlatformClipboard::registerNatives(QJniEnvironment &env)
+{
+ bool success = env.registerNativeMethods(Traits<QtClipboardManager>::className(),
+ { Q_JNI_NATIVE_SCOPED_METHOD(onClipboardDataChanged, QAndroidPlatformClipboard) });
+ if (!success) {
+ qCritical() << "QtClipboardManager: registerNativeMethods() failed";
+ return false;
+ }
+
+ return true;
+}
+
QT_END_NAMESPACE
#endif // QT_NO_CLIPBOARD
diff --git a/src/plugins/platforms/android/qandroidplatformclipboard.h b/src/plugins/platforms/android/qandroidplatformclipboard.h
index 9920b776d4..ab5c527f88 100644
--- a/src/plugins/platforms/android/qandroidplatformclipboard.h
+++ b/src/plugins/platforms/android/qandroidplatformclipboard.h
@@ -1,51 +1,20 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMCLIPBOARD_H
#define QANDROIDPLATFORMCLIPBOARD_H
#include <qpa/qplatformclipboard.h>
#include <QMimeData>
+#include <QtCore/qjnitypes.h>
#ifndef QT_NO_CLIPBOARD
+
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_CLASS(QtClipboardManager, "org/qtproject/qt/android/QtClipboardManager");
+
class QAndroidPlatformClipboard : public QPlatformClipboard
{
public:
@@ -54,8 +23,19 @@ public:
QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override;
void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override;
bool supportsMode(QClipboard::Mode mode) const override;
+
+ static bool registerNatives(QJniEnvironment &env);
+
private:
+ QMimeData *getClipboardMimeData();
+ void setClipboardMimeData(QMimeData *data);
+ void clearClipboardData();
+
+ static void onClipboardDataChanged(JNIEnv *env, jobject obj, jlong nativePointer);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(onClipboardDataChanged)
+
QMimeData *data = nullptr;
+ QtJniTypes::QtClipboardManager m_clipboardManager = nullptr;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp
index 334dd31d9a..2b9a1194d2 100644
--- a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp
+++ b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformdialoghelpers.h"
#include "androidjnimain.h"
@@ -48,16 +12,20 @@
QT_BEGIN_NAMESPACE
-namespace QtAndroidDialogHelpers {
-static jclass g_messageDialogHelperClass = 0;
+using namespace Qt::StringLiterals;
-static const char QtMessageHandlerHelperClassName[] = "org/qtproject/qt/android/QtMessageDialogHelper";
+namespace QtAndroidDialogHelpers {
+static jclass g_messageDialogHelperClass = nullptr;
QAndroidPlatformMessageDialogHelper::QAndroidPlatformMessageDialogHelper()
- :m_buttonId(-1)
- ,m_javaMessageDialog(g_messageDialogHelperClass, "(Landroid/app/Activity;)V", QtAndroid::activity())
- ,m_shown(false)
+ : m_javaMessageDialog(g_messageDialogHelperClass, "(Landroid/app/Activity;)V",
+ QtAndroidPrivate::activity().object())
+{
+}
+
+QAndroidPlatformMessageDialogHelper::~QAndroidPlatformMessageDialogHelper()
{
+ hide();
}
void QAndroidPlatformMessageDialogHelper::exec()
@@ -71,13 +39,13 @@ static QString htmlText(QString text)
{
if (Qt::mightBeRichText(text))
return text;
- text.remove(QLatin1Char('\r'));
- return text.toHtmlEscaped().replace(QLatin1Char('\n'), QLatin1String("<br />"));
+ text.remove(u'\r');
+ return text.toHtmlEscaped().replace(u'\n', "<br />"_L1);
}
-bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags
- , Qt::WindowModality windowModality
- , QWindow *parent)
+bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags,
+ Qt::WindowModality windowModality,
+ QWindow *parent)
{
Q_UNUSED(windowFlags);
Q_UNUSED(windowModality);
@@ -86,27 +54,38 @@ bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags
if (!opt.data())
return false;
- m_javaMessageDialog.callMethod<void>("setIcon", "(I)V", opt->icon());
+ if (!opt->checkBoxLabel().isNull())
+ return false; // Can't support
+
+ m_javaMessageDialog.callMethod<void>("setStandardIcon", "(I)V", opt->standardIcon());
QString str = htmlText(opt->windowTitle());
- if (!str.isEmpty())
- m_javaMessageDialog.callMethod<void>("setTile", "(Ljava/lang/String;)V", QJniObject::fromString(str).object());
+ if (!str.isEmpty()) {
+ m_javaMessageDialog.callMethod<void>("setTile", "(Ljava/lang/String;)V",
+ QJniObject::fromString(str).object());
+ }
str = htmlText(opt->text());
- if (!str.isEmpty())
- m_javaMessageDialog.callMethod<void>("setText", "(Ljava/lang/String;)V", QJniObject::fromString(str).object());
+ if (!str.isEmpty()) {
+ m_javaMessageDialog.callMethod<void>("setText", "(Ljava/lang/String;)V",
+ QJniObject::fromString(str).object());
+ }
str = htmlText(opt->informativeText());
- if (!str.isEmpty())
- m_javaMessageDialog.callMethod<void>("setInformativeText", "(Ljava/lang/String;)V", QJniObject::fromString(str).object());
+ if (!str.isEmpty()) {
+ m_javaMessageDialog.callMethod<void>("setInformativeText", "(Ljava/lang/String;)V",
+ QJniObject::fromString(str).object());
+ }
str = htmlText(opt->detailedText());
- if (!str.isEmpty())
- m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V", QJniObject::fromString(str).object());
+ if (!str.isEmpty()) {
+ m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V",
+ QJniObject::fromString(str).object());
+ }
- const int * currentLayout = buttonLayout(Qt::Horizontal, AndroidLayout);
+ const int *currentLayout = buttonLayout(Qt::Horizontal, AndroidLayout);
while (*currentLayout != QPlatformDialogHelper::EOL) {
- int role = (*currentLayout & ~QPlatformDialogHelper::Reverse);
+ const int role = (*currentLayout & ~QPlatformDialogHelper::Reverse);
addButtons(opt, static_cast<ButtonRole>(role));
++currentLayout;
}
@@ -116,7 +95,8 @@ bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags
return true;
}
-void QAndroidPlatformMessageDialogHelper::addButtons(QSharedPointer<QMessageDialogOptions> opt, ButtonRole role)
+void QAndroidPlatformMessageDialogHelper::addButtons(QSharedPointer<QMessageDialogOptions> opt,
+ ButtonRole role)
{
for (const QMessageDialogOptions::CustomButton &b : opt->customButtons()) {
if (b.role == role) {
@@ -131,7 +111,8 @@ void QAndroidPlatformMessageDialogHelper::addButtons(QSharedPointer<QMessageDial
StandardButton b = static_cast<StandardButton>(i);
if (buttonRole(b) == role && (opt->standardButtons() & i)) {
const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(b);
- m_javaMessageDialog.callMethod<void>("addButton", "(ILjava/lang/String;)V", i, QJniObject::fromString(text).object());
+ m_javaMessageDialog.callMethod<void>("addButton", "(ILjava/lang/String;)V", i,
+ QJniObject::fromString(text).object());
}
}
}
@@ -144,17 +125,17 @@ void QAndroidPlatformMessageDialogHelper::hide()
void QAndroidPlatformMessageDialogHelper::dialogResult(int buttonID)
{
- m_buttonId = buttonID;
if (m_loop.isRunning())
m_loop.exit();
- if (m_buttonId < 0) {
+ if (buttonID < 0) {
emit reject();
return;
}
- QPlatformDialogHelper::StandardButton standardButton = static_cast<QPlatformDialogHelper::StandardButton>(buttonID);
- QPlatformDialogHelper::ButtonRole role = QPlatformDialogHelper::buttonRole(standardButton);
+ const StandardButton standardButton = static_cast<StandardButton>(buttonID);
+ ButtonRole role = QPlatformDialogHelper::buttonRole(standardButton);
if (buttonID > QPlatformDialogHelper::LastButton) {
+ // In case of a custom button
const QMessageDialogOptions::CustomButton *custom = options()->customButton(buttonID);
Q_ASSERT(custom);
role = custom->role;
@@ -169,7 +150,7 @@ static void dialogResult(JNIEnv * /*env*/, jobject /*thiz*/, jlong handler, int
QMetaObject::invokeMethod(object, "dialogResult", Qt::QueuedConnection, Q_ARG(int, buttonID));
}
-static JNINativeMethod methods[] = {
+static const JNINativeMethod methods[] = {
{"dialogResult", "(JI)V", (void *)dialogResult}
};
@@ -181,20 +162,19 @@ static JNINativeMethod methods[] = {
return false; \
}
-bool registerNatives(JNIEnv *env)
+bool registerNatives(QJniEnvironment &env)
{
- QJniEnvironment qenv;
- jclass clazz = qenv.findClass(QtMessageHandlerHelperClassName);
+ const char QtMessageHandlerHelperClassName[] = "org/qtproject/qt/android/QtMessageDialogHelper";
+ jclass clazz = env.findClass(QtMessageHandlerHelperClassName);
if (!clazz) {
__android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), QtAndroid::classErrorMsgFmt()
, QtMessageHandlerHelperClassName);
return false;
}
g_messageDialogHelperClass = static_cast<jclass>(env->NewGlobalRef(clazz));
- FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNativeDialogHelper");
- jclass appClass = static_cast<jclass>(env->NewGlobalRef(clazz));
- if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+ if (!env.registerNativeMethods("org/qtproject/qt/android/QtNativeDialogHelper",
+ methods, sizeof(methods) / sizeof(methods[0]))) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "RegisterNatives failed");
return false;
}
diff --git a/src/plugins/platforms/android/qandroidplatformdialoghelpers.h b/src/plugins/platforms/android/qandroidplatformdialoghelpers.h
index 1953b842d5..86f1cf77e7 100644
--- a/src/plugins/platforms/android/qandroidplatformdialoghelpers.h
+++ b/src/plugins/platforms/android/qandroidplatformdialoghelpers.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2013 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMDIALOGHELPERS_H
#define QANDROIDPLATFORMDIALOGHELPERS_H
@@ -44,12 +8,13 @@
#include <jni.h>
#include <QEventLoop>
-#include <QtCore/QJniEnvironment>
#include <QtCore/QJniObject>
#include <qpa/qplatformdialoghelper.h>
QT_BEGIN_NAMESPACE
+class QJniEnvironment;
+
namespace QtAndroidDialogHelpers {
class QAndroidPlatformMessageDialogHelper: public QPlatformMessageDialogHelper
@@ -57,9 +22,10 @@ class QAndroidPlatformMessageDialogHelper: public QPlatformMessageDialogHelper
Q_OBJECT
public:
QAndroidPlatformMessageDialogHelper();
+ ~QAndroidPlatformMessageDialogHelper();
+
void exec() override;
- bool show(Qt::WindowFlags windowFlags,
- Qt::WindowModality windowModality,
+ bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality,
QWindow *parent) override;
void hide() override;
@@ -70,14 +36,13 @@ private:
void addButtons(QSharedPointer<QMessageDialogOptions> opt, ButtonRole role);
private:
- int m_buttonId;
+ bool m_shown = false;
QEventLoop m_loop;
QJniObject m_javaMessageDialog;
- bool m_shown;
};
-bool registerNatives(JNIEnv *env);
+bool registerNatives(QJniEnvironment &env);
}
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
index 7741fc755d..d8a5c58d2c 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformfiledialoghelper.h"
@@ -46,9 +10,12 @@
#include <QMimeDatabase>
#include <QMimeType>
#include <QRegularExpression>
+#include <QUrl>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace QtAndroidFileDialogHelper {
#define RESULT_OK -1
@@ -58,7 +25,7 @@ const char JniIntentClass[] = "android/content/Intent";
QAndroidPlatformFileDialogHelper::QAndroidPlatformFileDialogHelper()
: QPlatformFileDialogHelper(),
- m_activity(QtAndroid::activity())
+ m_activity(QtAndroidPrivate::activity())
{
}
@@ -78,7 +45,8 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji
if (uri.isValid()) {
takePersistableUriPermission(uri);
m_selectedFile.append(QUrl(uri.toString()));
- Q_EMIT fileSelected(m_selectedFile.first());
+ Q_EMIT fileSelected(m_selectedFile.constFirst());
+ Q_EMIT currentChanged(m_selectedFile.constFirst());
Q_EMIT accept();
return true;
@@ -97,6 +65,7 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji
m_selectedFile.append(itemUri.toString());
}
Q_EMIT filesSelected(m_selectedFile);
+ Q_EMIT currentChanged(m_selectedFile.constFirst());
Q_EMIT accept();
}
@@ -119,7 +88,7 @@ void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJniOb
uri.object(), modeFlags);
}
-void QAndroidPlatformFileDialogHelper::setIntentTitle(const QString &title)
+void QAndroidPlatformFileDialogHelper::setInitialFileName(const QString &title)
{
const QJniObject extraTitle = QJniObject::getStaticObjectField(
JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;");
@@ -128,6 +97,22 @@ void QAndroidPlatformFileDialogHelper::setIntentTitle(const QString &title)
extraTitle.object(), QJniObject::fromString(title).object());
}
+void QAndroidPlatformFileDialogHelper::setInitialDirectoryUri(const QString &directory)
+{
+ if (directory.isEmpty())
+ return;
+
+ if (QNativeInterface::QAndroidApplication::sdkVersion() < 26)
+ return;
+
+ const auto extraInitialUri = QJniObject::getStaticObjectField(
+ "android/provider/DocumentsContract", "EXTRA_INITIAL_URI", "Ljava/lang/String;");
+ m_intent.callObjectMethod("putExtra",
+ "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;",
+ extraInitialUri.object(),
+ QJniObject::fromString(directory).object());
+}
+
void QAndroidPlatformFileDialogHelper::setOpenableCategory()
{
const QJniObject CATEGORY_OPENABLE = QJniObject::getStaticObjectField(
@@ -161,15 +146,18 @@ void QAndroidPlatformFileDialogHelper::setMimeTypes()
{
QStringList mimeTypes = options()->mimeTypeFilters();
const QStringList nameFilters = options()->nameFilters();
- const QString nameFilter = nameFilters.isEmpty() ? QString() : nameFilters.first();
- if (!nameFilter.isEmpty()) {
+ if (!nameFilters.isEmpty()) {
QMimeDatabase db;
- for (const QString &filter : nameFilterExtensions(nameFilter))
- mimeTypes.append(db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension).name());
+ for (auto filter : nameFilters) {
+ if (!filter.isEmpty()) {
+ for (const QString &filter : nameFilterExtensions(filter))
+ mimeTypes.append(db.mimeTypeForFile(filter, QMimeDatabase::MatchExtension).name());
+ }
+ }
}
- const QString initialType = mimeTypes.size() == 1 ? mimeTypes.at(0) : QLatin1String("*/*");
+ const QString initialType = mimeTypes.size() == 1 ? mimeTypes.at(0) : "*/*"_L1;
m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;",
QJniObject::fromString(initialType).object());
@@ -209,6 +197,9 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
if (options()->acceptMode() == QFileDialogOptions::AcceptSave) {
m_intent = getFileDialogIntent("ACTION_CREATE_DOCUMENT");
+ const QList<QUrl> selectedFiles = options()->initiallySelectedFiles();
+ if (selectedFiles.size() > 0)
+ setInitialFileName(selectedFiles.first().fileName());
} else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
switch (options()->fileMode()) {
case QFileDialogOptions::FileMode::DirectoryOnly:
@@ -232,7 +223,7 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
setMimeTypes();
}
- setIntentTitle(options()->windowTitle());
+ setInitialDirectoryUri(m_directory.toString());
QtAndroidPrivate::registerActivityResultListener(this);
m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
@@ -247,6 +238,11 @@ void QAndroidPlatformFileDialogHelper::hide()
QtAndroidPrivate::unregisterActivityResultListener(this);
}
+void QAndroidPlatformFileDialogHelper::setDirectory(const QUrl &directory)
+{
+ m_directory = directory;
+}
+
void QAndroidPlatformFileDialogHelper::exec()
{
m_eventLoop.exec(QEventLoop::DialogExec);
diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
index 2d3cb49a96..156eda9142 100644
--- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
+++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB)
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMFILEDIALOGHELPER_H
#define QANDROIDPLATFORMFILEDIALOGHELPER_H
@@ -68,21 +32,23 @@ public:
void setFilter() override {}
QList<QUrl> selectedFiles() const override { return m_selectedFile; }
void selectFile(const QUrl &) override {}
- QUrl directory() const override { return QUrl(); }
- void setDirectory(const QUrl &) override {}
+ QUrl directory() const override { return m_directory; }
+ void setDirectory(const QUrl &directory) override;
bool defaultNameFilterDisables() const override { return false; }
bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override;
private:
QJniObject getFileDialogIntent(const QString &intentType);
void takePersistableUriPermission(const QJniObject &uri);
- void setIntentTitle(const QString &title);
+ void setInitialFileName(const QString &title);
+ void setInitialDirectoryUri(const QString &directory);
void setOpenableCategory();
void setAllowMultipleSelections(bool allowMultiple);
void setMimeTypes();
QEventLoop m_eventLoop;
QList<QUrl> m_selectedFile;
+ QUrl m_directory;
QJniObject m_intent;
const QJniObject m_activity;
};
diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp
index 2fdf269566..82a10dac07 100644
--- a/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp
+++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.cpp
@@ -1,51 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDir>
+#include <QLocale>
#include "qandroidplatformfontdatabase.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QString QAndroidPlatformFontDatabase::fontDir() const
{
- return QLatin1String("/system/fonts");
+ return "/system/fonts"_L1;
+}
+
+QFont QAndroidPlatformFontDatabase::defaultFont() const
+{
+ return QFont("Roboto"_L1);
}
void QAndroidPlatformFontDatabase::populateFontDatabase()
@@ -59,9 +31,9 @@ void QAndroidPlatformFontDatabase::populateFontDatabase()
}
QStringList nameFilters;
- nameFilters << QLatin1String("*.ttf")
- << QLatin1String("*.otf")
- << QLatin1String("*.ttc");
+ nameFilters << "*.ttf"_L1
+ << "*.otf"_L1
+ << "*.ttc"_L1;
const auto entries = dir.entryInfoList(nameFilters, QDir::Files);
for (const QFileInfo &fi : entries) {
@@ -76,12 +48,44 @@ QStringList QAndroidPlatformFontDatabase::fallbacksForFamily(const QString &fami
QChar::Script script) const
{
QStringList result;
+
+ // Prepend CJK fonts by the locale.
+ QLocale locale = QLocale::system();
+ switch (locale.language()) {
+ case QLocale::Chinese: {
+ switch (locale.territory()) {
+ case QLocale::China:
+ case QLocale::Singapore:
+ result.append(QStringLiteral("Noto Sans Mono CJK SC"));
+ break;
+ case QLocale::Taiwan:
+ case QLocale::HongKong:
+ case QLocale::Macao:
+ result.append(QStringLiteral("Noto Sans Mono CJK TC"));
+ break;
+ default:
+ // no modifications.
+ break;
+ }
+ break;
+ }
+ case QLocale::Japanese:
+ result.append(QStringLiteral("Noto Sans Mono CJK JP"));
+ break;
+ case QLocale::Korean:
+ result.append(QStringLiteral("Noto Sans Mono CJK KR"));
+ break;
+ default:
+ // no modifications.
+ break;
+ }
+
if (styleHint == QFont::Monospace || styleHint == QFont::Courier)
- result.append(QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(QLatin1Char(';')));
+ result.append(QString(qgetenv("QT_ANDROID_FONTS_MONOSPACE")).split(u';'));
else if (styleHint == QFont::Serif)
- result.append(QString(qgetenv("QT_ANDROID_FONTS_SERIF")).split(QLatin1Char(';')));
+ result.append(QString(qgetenv("QT_ANDROID_FONTS_SERIF")).split(u';'));
else
- result.append(QString(qgetenv("QT_ANDROID_FONTS")).split(QLatin1Char(';')));
+ result.append(QString(qgetenv("QT_ANDROID_FONTS")).split(u';'));
result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script));
return result;
diff --git a/src/plugins/platforms/android/qandroidplatformfontdatabase.h b/src/plugins/platforms/android/qandroidplatformfontdatabase.h
index 691656d89d..23561e2214 100644
--- a/src/plugins/platforms/android/qandroidplatformfontdatabase.h
+++ b/src/plugins/platforms/android/qandroidplatformfontdatabase.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMFONTDATABASE_H
#define QANDROIDPLATFORMFONTDATABASE_H
@@ -49,6 +13,7 @@ class QAndroidPlatformFontDatabase: public QFreeTypeFontDatabase
public:
QString fontDir() const override;
void populateFontDatabase() override;
+ QFont defaultFont() const override;
QStringList fallbacksForFamily(const QString &family,
QFont::Style style,
QFont::StyleHint styleHint,
diff --git a/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp b/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp
index 1c920c0af9..e84a481a2b 100644
--- a/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp
+++ b/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp
@@ -1,125 +1,106 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformforeignwindow.h"
#include "androidjnimain.h"
#include <QtCore/qvariant.h>
#include <qpa/qwindowsysteminterface.h>
#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qjnitypes.h>
QT_BEGIN_NAMESPACE
QAndroidPlatformForeignWindow::QAndroidPlatformForeignWindow(QWindow *window, WId nativeHandle)
- : QAndroidPlatformWindow(window),
- m_surfaceId(-1)
+ : QAndroidPlatformWindow(window), m_view(nullptr), m_nativeViewInserted(false)
{
m_view = reinterpret_cast<jobject>(nativeHandle);
- if (m_view.isValid())
- QtAndroid::setViewVisibility(m_view.object(), false);
-}
+ if (isEmbeddingContainer()) {
+ m_nativeViewId = m_view.callMethod<jint>("getId");
+ return;
+ }
-QAndroidPlatformForeignWindow::~QAndroidPlatformForeignWindow()
-{
if (m_view.isValid())
QtAndroid::setViewVisibility(m_view.object(), false);
- if (m_surfaceId != -1)
- QtAndroid::destroySurface(m_surfaceId);
}
-void QAndroidPlatformForeignWindow::lower()
+QAndroidPlatformForeignWindow::~QAndroidPlatformForeignWindow()
{
- if (m_surfaceId == -1)
+ if (isEmbeddingContainer())
return;
- QAndroidPlatformWindow::lower();
- QtAndroid::bringChildToBack(m_surfaceId);
-}
+ if (m_view.isValid())
+ QtAndroid::setViewVisibility(m_view.object(), false);
-void QAndroidPlatformForeignWindow::raise()
-{
- if (m_surfaceId == -1)
- return;
+ m_nativeQtWindow.callMethod<void>("removeNativeView");
- QAndroidPlatformWindow::raise();
- QtAndroid::bringChildToFront(m_surfaceId);
}
void QAndroidPlatformForeignWindow::setGeometry(const QRect &rect)
{
QAndroidPlatformWindow::setGeometry(rect);
- if (m_surfaceId != -1)
- QtAndroid::setSurfaceGeometry(m_surfaceId, rect);
+ if (isEmbeddingContainer())
+ return;
+
+ if (m_nativeViewInserted)
+ setNativeGeometry(rect);
}
void QAndroidPlatformForeignWindow::setVisible(bool visible)
{
+ if (isEmbeddingContainer()) {
+ QAndroidPlatformWindow::setVisible(visible);
+ return;
+ }
+
if (!m_view.isValid())
return;
QtAndroid::setViewVisibility(m_view.object(), visible);
- QAndroidPlatformWindow::setVisible(visible);
- if (!visible && m_surfaceId != -1) {
- QtAndroid::destroySurface(m_surfaceId);
- m_surfaceId = -1;
- } else if (m_surfaceId == -1) {
- m_surfaceId = QtAndroid::insertNativeView(m_view.object(), geometry());
+ if (!visible && m_nativeViewInserted) {
+ m_nativeQtWindow.callMethod<void>("removeNativeView");
+ m_nativeViewInserted = false;
+ } else if (!m_nativeViewInserted) {
+ addViewToWindow();
}
}
void QAndroidPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState state)
{
- if (state <= Qt::ApplicationHidden
- && m_surfaceId != -1) {
- QtAndroid::destroySurface(m_surfaceId);
- m_surfaceId = -1;
- } else if (m_view.isValid() && m_surfaceId == -1){
- m_surfaceId = QtAndroid::insertNativeView(m_view.object(), geometry());
+ if (!isEmbeddingContainer()) {
+ if (state <= Qt::ApplicationHidden
+ && m_nativeViewInserted) {
+ m_nativeQtWindow.callMethod<void>("removeNativeView");
+ m_nativeViewInserted = false;
+ } else if (m_view.isValid() && !m_nativeViewInserted){
+ addViewToWindow();
+ }
}
QAndroidPlatformWindow::applicationStateChanged(state);
}
-void QAndroidPlatformForeignWindow::setParent(const QPlatformWindow *window)
+WId QAndroidPlatformForeignWindow::winId() const
{
- Q_UNUSED(window);
+ if (isEmbeddingContainer() && m_view.isValid())
+ return reinterpret_cast<WId>(m_view.object());
+ if (m_nativeQtWindow.isValid())
+ return reinterpret_cast<WId>(m_nativeQtWindow.object());
+ return 0L;
+}
+
+void QAndroidPlatformForeignWindow::addViewToWindow()
+{
+ if (isEmbeddingContainer())
+ return;
+
+ jint x = 0, y = 0, w = -1, h = -1;
+ if (!geometry().isNull())
+ geometry().getRect(&x, &y, &w, &h);
+
+ m_nativeQtWindow.callMethod<void>("setNativeView", m_view, x, y, qMax(w, 1), qMax(h, 1));
+ m_nativeViewInserted = true;
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformforeignwindow.h b/src/plugins/platforms/android/qandroidplatformforeignwindow.h
index 8e45395838..503524cced 100644
--- a/src/plugins/platforms/android/qandroidplatformforeignwindow.h
+++ b/src/plugins/platforms/android/qandroidplatformforeignwindow.h
@@ -1,68 +1,34 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMFOREIGNWINDOW_H
#define QANDROIDPLATFORMFOREIGNWINDOW_H
-#include "androidsurfaceclient.h"
#include "qandroidplatformwindow.h"
#include <QtCore/QJniObject>
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_CLASS(View, "android/view/View")
+
class QAndroidPlatformForeignWindow : public QAndroidPlatformWindow
{
public:
explicit QAndroidPlatformForeignWindow(QWindow *window, WId nativeHandle);
~QAndroidPlatformForeignWindow();
- void lower() override;
- void raise() override;
void setGeometry(const QRect &rect) override;
void setVisible(bool visible) override;
void applicationStateChanged(Qt::ApplicationState state) override;
- void setParent(const QPlatformWindow *window) override;
bool isForeignWindow() const override { return true; }
+ WId winId() const override;
+
private:
- int m_surfaceId;
- QJniObject m_view;
+ void addViewToWindow();
+
+ QtJniTypes::View m_view;
+ bool m_nativeViewInserted;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformiconengine.cpp b/src/plugins/platforms/android/qandroidplatformiconengine.cpp
new file mode 100644
index 0000000000..faa63dcca1
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformiconengine.cpp
@@ -0,0 +1,616 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qandroidplatformiconengine.h"
+#include "androidjnimain.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qjniarray.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qset.h>
+
+#include <QtGui/qfontdatabase.h>
+#include <QtGui/qpainter.h>
+#include <QtGui/qpalette.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+Q_LOGGING_CATEGORY(lcIconEngineFontDownload, "qt.qpa.iconengine.fontdownload")
+
+// the primary types to work with the FontRequest API
+Q_DECLARE_JNI_CLASS(FontRequest, "androidx/core/provider/FontRequest")
+Q_DECLARE_JNI_CLASS(FontsContractCompat, "androidx/core/provider/FontsContractCompat")
+Q_DECLARE_JNI_CLASS(FontFamilyResult, "androidx/core/provider/FontsContractCompat$FontFamilyResult")
+Q_DECLARE_JNI_CLASS(FontInfo, "androidx/core/provider/FontsContractCompat$FontInfo")
+
+// various utility types
+Q_DECLARE_JNI_CLASS(List, "java/util/List"); // List is just an Interface
+Q_DECLARE_JNI_CLASS(ArrayList, "java/util/ArrayList");
+Q_DECLARE_JNI_CLASS(HashSet, "java/util/HashSet");
+Q_DECLARE_JNI_CLASS(Uri, "android/net/Uri")
+Q_DECLARE_JNI_CLASS(CancellationSignal, "android/os/CancellationSignal")
+Q_DECLARE_JNI_CLASS(ParcelFileDescriptor, "android/os/ParcelFileDescriptor")
+Q_DECLARE_JNI_CLASS(ContentResolver, "android/content/ContentResolver")
+Q_DECLARE_JNI_CLASS(PackageManager, "android/content/pm/PackageManager")
+Q_DECLARE_JNI_CLASS(ProviderInfo, "android/content/pm/ProviderInfo")
+Q_DECLARE_JNI_CLASS(PackageInfo, "android/content/pm/PackageInfo")
+Q_DECLARE_JNI_CLASS(Signature, "android/content/pm/Signature")
+
+namespace FontProvider {
+
+static QString fetchFont(const QString &query)
+{
+ using namespace QtJniTypes;
+
+ static QMap<QString, QString> triedFonts;
+ const auto it = triedFonts.find(query);
+ if (it != triedFonts.constEnd())
+ return it.value();
+
+ QString fontFamily;
+ triedFonts[query] = fontFamily; // mark as tried
+
+ QStringList loadedFamilies;
+ if (QFile file(query); file.open(QIODevice::ReadOnly)) {
+ qCDebug(lcIconEngineFontDownload) << "Loading font from resource" << query;
+ const QByteArray fontData = file.readAll();
+ int fontId = QFontDatabase::addApplicationFontFromData(fontData);
+ loadedFamilies << QFontDatabase::applicationFontFamilies(fontId);
+ } else if (!query.startsWith(u":/"_s)) {
+ const QString package = u"com.google.android.gms"_s;
+ const QString authority = u"com.google.android.gms.fonts"_s;
+
+ // First we access the content provider to get the signatures of the authority for the package
+ const auto context = QtAndroidPrivate::context();
+
+ auto packageManager = context.callMethod<PackageManager>("getPackageManager");
+ if (!packageManager.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to instantiate PackageManager");
+ return fontFamily;
+ }
+ const int signaturesField = PackageManager::getStaticField<int>("GET_SIGNATURES");
+ auto providerInfo = packageManager.callMethod<ProviderInfo>("resolveContentProvider",
+ authority, 0);
+ if (!providerInfo.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to resolve content provider");
+ return fontFamily;
+ }
+ const QString packageName = providerInfo.getField<QString>("packageName");
+ if (packageName != package) {
+ qCWarning(lcIconEngineFontDownload, "Mismatched provider package - expected '%s', got '%s'",
+ package.toUtf8().constData(), packageName.toUtf8().constData());
+ return fontFamily;
+ }
+ auto packageInfo = packageManager.callMethod<PackageInfo>("getPackageInfo",
+ package, signaturesField);
+ if (!packageInfo.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to get package info with signature field %d",
+ signaturesField);
+ return fontFamily;
+ }
+ const auto signatures = packageInfo.getField<Signature[]>("signatures");
+ if (!signatures.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to get signature array from package info");
+ return fontFamily;
+ }
+
+ // FontRequest wants a list of sets for the certificates
+ ArrayList outerList;
+ HashSet innerSet;
+ Q_ASSERT(outerList.isValid() && innerSet.isValid());
+
+ for (const auto &signature : signatures) {
+ const QJniArray<jbyte> byteArray = signature.callMethod<jbyte[]>("toByteArray");
+
+ // add takes an Object, not an Array
+ if (!innerSet.callMethod<jboolean>("add", byteArray.object<jobject>()))
+ qCWarning(lcIconEngineFontDownload, "Failed to add signature to set");
+ }
+ // Add the set to the list
+ if (!outerList.callMethod<jboolean>("add", innerSet.object()))
+ qCWarning(lcIconEngineFontDownload, "Failed to add set to certificate list");
+
+ // FontRequest constructor wants a List interface, not an ArrayList
+ FontRequest fontRequest(authority, package, query, outerList.object<List>());
+ if (!fontRequest.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to create font request for '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ // Call FontsContractCompat::fetchFonts with the FontRequest object
+ auto fontFamilyResult = FontsContractCompat::callStaticMethod<FontFamilyResult>(
+ "fetchFonts",
+ context,
+ CancellationSignal(nullptr),
+ fontRequest);
+ if (!fontFamilyResult.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Failed to fetch fonts for query '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ enum class StatusCode {
+ OK = 0,
+ UNEXPECTED_DATA_PROVIDED = 1,
+ WRONG_CERTIFICATES = 2,
+ };
+
+ const StatusCode statusCode = fontFamilyResult.callMethod<StatusCode>("getStatusCode");
+ switch (statusCode) {
+ case StatusCode::OK:
+ break;
+ case StatusCode::UNEXPECTED_DATA_PROVIDED:
+ qCWarning(lcIconEngineFontDownload, "Provider returned unexpected data for query '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ case StatusCode::WRONG_CERTIFICATES:
+ qCWarning(lcIconEngineFontDownload, "Wrong Certificates provided in query '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ const auto fontInfos = fontFamilyResult.callMethod<FontInfo[]>("getFonts");
+ if (!fontInfos.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "FontFamilyResult::getFonts returned null object for '%s'",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+
+ auto contentResolver = context.callMethod<ContentResolver>("getContentResolver");
+
+ for (QJniObject fontInfo : fontInfos) {
+ if (!fontInfo.isValid()) {
+ qCDebug(lcIconEngineFontDownload, "Received null-fontInfo object, skipping");
+ continue;
+ }
+ enum class ResultCode {
+ OK = 0,
+ FONT_NOT_FOUND = 1,
+ FONT_UNAVAILABLE = 2,
+ MALFORMED_QUERY = 3,
+ };
+ const ResultCode resultCode = fontInfo.callMethod<ResultCode>("getResultCode");
+ switch (resultCode) {
+ case ResultCode::OK:
+ break;
+ case ResultCode::FONT_NOT_FOUND:
+ qCWarning(lcIconEngineFontDownload, "Font '%s' could not be found",
+ query.toUtf8().constData());
+ return fontFamily;
+ case ResultCode::FONT_UNAVAILABLE:
+ qCWarning(lcIconEngineFontDownload, "Font '%s' is unavailable at",
+ query.toUtf8().constData());
+ return fontFamily;
+ case ResultCode::MALFORMED_QUERY:
+ qCWarning(lcIconEngineFontDownload, "Query string '%s' is malformed",
+ query.toUtf8().constData());
+ return fontFamily;
+ }
+ auto fontUri = fontInfo.callMethod<Uri>("getUri");
+ // in this case the Font URI is always a content scheme file, made
+ // so the app requesting it has permissions to open
+ auto fileDescriptor = contentResolver.callMethod<ParcelFileDescriptor>("openFileDescriptor",
+ fontUri, u"r"_s);
+ if (!fileDescriptor.isValid()) {
+ qCWarning(lcIconEngineFontDownload, "Font file '%s' not accessible",
+ fontUri.toString().toUtf8().constData());
+ continue;
+ }
+
+ int fd = fileDescriptor.callMethod<int>("detachFd");
+ QFile file;
+ file.open(fd, QFile::OpenModeFlag::ReadOnly, QFile::FileHandleFlag::AutoCloseHandle);
+ const QByteArray fontData = file.readAll();
+ qCDebug(lcIconEngineFontDownload) << "Font file read:" << fontData.size() << "bytes";
+ int fontId = QFontDatabase::addApplicationFontFromData(fontData);
+ loadedFamilies << QFontDatabase::applicationFontFamilies(fontId);
+ }
+ }
+
+ qCDebug(lcIconEngineFontDownload) << "Query '" << query << "' added families" << loadedFamilies;
+ if (!loadedFamilies.isEmpty())
+ fontFamily = loadedFamilies.first();
+ triedFonts[query] = fontFamily;
+ return fontFamily;
+}
+}
+
+QString QAndroidPlatformIconEngine::glyphs() const
+{
+ if (!QFontInfo(m_iconFont).exactMatch())
+ return {};
+
+ static constexpr std::pair<QLatin1StringView, QStringView> glyphMap[] = {
+ {"address-book-new"_L1, u"\ue0e0"},
+ {"application-exit"_L1, u"\ue5cd"},
+ {"appointment-new"_L1, u"\ue878"},
+ {"call-start"_L1, u"\ue0b0"},
+ {"call-stop"_L1, u"\ue0b1"},
+ {"contact-new"_L1, u"\uf22e"},
+ {"document-new"_L1, u"\ue89c"},
+ {"document-open"_L1, u"\ue2c8"},
+ {"document-open-recent"_L1, u"\ue4a7"},
+ {"document-page-setup"_L1, u"\uf88c"},
+ {"document-print"_L1, u"\ue8ad"},
+ {"document-print-preview"_L1, u"\uefb2"},
+ {"document-properties"_L1, u"\uf775"},
+ {"document-revert"_L1, u"\ue929"},
+ {"document-save"_L1, u"\ue161"},
+ {"document-save-as"_L1, u"\ueb60"},
+ {"document-send"_L1, u"\uf09b"},
+ {"edit-clear"_L1, u"\ue872"},
+ {"edit-copy"_L1, u"\ue14d"},
+ {"edit-cut"_L1, u"\ue14e"},
+ {"edit-delete"_L1, u"\ue14a"},
+ {"edit-find"_L1, u"\ue8b6"},
+ {"edit-find-replace"_L1, u"\ue881"},
+ {"edit-paste"_L1, u"\ue14f"},
+ {"edit-redo"_L1, u"\ue15a"},
+ {"edit-select-all"_L1, u"\ue162"},
+ {"edit-undo"_L1, u"\ue166"},
+ {"folder-new"_L1, u"\ue2cc"},
+ {"format-indent-less"_L1, u"\ue23d"},
+ {"format-indent-more"_L1, u"\ue23e"},
+ {"format-justify-center"_L1, u"\ue234"},
+ {"format-justify-fill"_L1, u"\ue235"},
+ {"format-justify-left"_L1, u"\ue236"},
+ {"format-justify-right"_L1, u"\ue237"},
+ {"format-text-direction-ltr"_L1, u"\ue247"},
+ {"format-text-direction-rtl"_L1, u"\ue248"},
+ {"format-text-bold"_L1, u"\ue238"},
+ {"format-text-italic"_L1, u"\ue23f"},
+ {"format-text-underline"_L1, u"\ue249"},
+ {"format-text-strikethrough"_L1, u"\ue246"},
+ {"go-bottom"_L1,u"\ue258"},
+ {"go-down"_L1,u"\uf1e3"},
+ {"go-first"_L1, u"\ue5dc"},
+ {"go-home"_L1, u"\ue88a"},
+ {"go-jump"_L1, u"\uf719"},
+ {"go-last"_L1, u"\ue5dd"},
+ {"go-next"_L1, u"\ue5c8"},
+ {"go-previous"_L1, u"\ue5c4"},
+ {"go-top"_L1, u"\ue25a"},
+ {"go-up"_L1, u"\uf1e0"},
+ {"help-about"_L1, u"\ue88e"},
+ {"help-contents"_L1, u"\ue8de"},
+ {"help-faq"_L1, u"\uf04c"},
+ {"insert-image"_L1, u"\ue43e"},
+ {"insert-link"_L1, u"\ue178"},
+ //{"insert-object"_L1, u"\u"},
+ {"insert-text"_L1, u"\uf827"},
+ {"list-add"_L1, u"\ue145"},
+ {"list-remove"_L1, u"\ue15b"},
+ {"mail-forward"_L1, u"\ue154"},
+ {"mail-mark-important"_L1, u"\ue937"},
+ //{"mail-mark-junk"_L1, u"\u"},
+ //{"mail-mark-notjunk"_L1, u"\u"},
+ {"mail-mark-read"_L1, u"\uf18c"},
+ {"mail-mark-unread"_L1, u"\ue9bc"},
+ {"mail-message-new"_L1, u"\ue3c9"},
+ {"mail-reply-all"_L1, u"\ue15f"},
+ {"mail-reply-sender"_L1, u"\ue15e"},
+ {"mail-send"_L1, u"\ue163"},
+ //{"mail-send-receive"_L1, u"\u"},
+ {"media-eject"_L1, u"\ue8fb"},
+ {"media-playback-pause"_L1, u"\ue034"},
+ {"media-playback-start"_L1, u"\ue037"},
+ {"media-playback-stop"_L1, u"\ue047"},
+ {"media-record"_L1, u"\uf679"},
+ {"media-seek-backward"_L1, u"\ue020"},
+ {"media-seek-forward"_L1, u"\ue01f"},
+ {"media-skip-backward"_L1, u"\ue045"},
+ {"media-skip-forward"_L1, u"\ue044"},
+ //{"object-flip-horizontal"_L1, u"\u"},
+ //{"object-flip-vertical"_L1, u"\u"},
+ {"object-rotate-left"_L1, u"\ue419"},
+ {"object-rotate-right"_L1, u"\ue41a"},
+ {"process-stop"_L1, u"\ue5c9"},
+ {"system-lock-screen"_L1, u"\ue897"},
+ {"system-log-out"_L1, u"\ue9ba"},
+ //{"system-run"_L1, u"\u"},
+ {"system-search"_L1, u"\uef70"},
+ {"system-reboot"_L1, u"\uf053"},
+ {"system-shutdown"_L1, u"\ue8ac"},
+ {"tools-check-spelling"_L1, u"\ue8ce"},
+ {"view-fullscreen"_L1, u"\ue5d0"},
+ {"view-refresh"_L1, u"\ue5d5"},
+ {"view-restore"_L1, u"\uf1cf"},
+ {"view-sort-ascending"_L1, u"\ue25a"},
+ {"view-sort-descending"_L1, u"\ue258"},
+ {"window-close"_L1, u"\ue5cd"},
+ {"window-new"_L1, u"\uf710"},
+ {"zoom-fit-best"_L1, u"\uea10"},
+ {"zoom-in"_L1, u"\ue8ff"},
+ {"zoom-original"_L1, u"\ue5d1"},
+ {"zoom-out"_L1, u"\ue900"},
+ {"process-working"_L1, u"\uef64"},
+ {"accessories-calculator"_L1, u"\uea5f"},
+ {"accessories-character-map"_L1, u"\uf8a3"},
+ {"accessories-dictionary"_L1, u"\uf539"},
+ {"accessories-text-editor"_L1, u"\ue262"},
+ {"help-browser"_L1, u"\ue887"},
+ {"multimedia-volume-control"_L1, u"\ue050"},
+ {"preferences-desktop-accessibility"_L1, u"\uf05d"},
+ {"preferences-desktop-font"_L1, u"\ue165"},
+ {"preferences-desktop-keyboard"_L1, u"\ue312"},
+ //{"preferences-desktop-locale"_L1, u"\u"},
+ {"preferences-desktop-multimedia"_L1, u"\uea75"},
+ //{"preferences-desktop-screensaver"_L1, u"\u"},
+ {"preferences-desktop-theme"_L1, u"\uf560"},
+ {"preferences-desktop-wallpaper"_L1, u"\ue1bc"},
+ {"system-file-manager"_L1, u"\ue2c7"},
+ {"system-software-install"_L1, u"\ueb71"},
+ {"system-software-update"_L1, u"\ue8d7"},
+ {"utilities-system-monitor"_L1, u"\uef5b"},
+ {"utilities-terminal"_L1, u"\ueb8e"},
+ //{"applications-accessories"_L1, u"\u"},
+ {"applications-development"_L1, u"\ue720"},
+ {"applications-engineering"_L1, u"\uea3d"},
+ {"applications-games"_L1, u"\uf135"},
+ //{"applications-graphics"_L1, u"\u"},
+ {"applications-internet"_L1, u"\ue80b"},
+ {"applications-multimedia"_L1, u"\uf06a"},
+ //{"applications-office"_L1, u"\u"},
+ //{"applications-other"_L1, u"\u"},
+ {"applications-science"_L1, u"\uea4b"},
+ //{"applications-system"_L1, u"\u"},
+ //{"applications-utilities"_L1, u"\u"},
+ {"preferences-desktop"_L1, u"\ueb97"},
+ //{"preferences-desktop-peripherals"_L1, u"\u"},
+ {"preferences-desktop-personal"_L1, u"\uf835"},
+ //{"preferences-other"_L1, u"\u"},
+ {"preferences-system"_L1, u"\ue8b8"},
+ {"preferences-system-network"_L1, u"\ue894"},
+ {"system-help"_L1, u"\ue887"},
+ //{"audio-card"_L1, u"\u"},
+ {"audio-input-microphone"_L1, u"\ue029"},
+ {"battery"_L1, u"\ue1a4"},
+ {"camera-photo"_L1, u"\ue412"},
+ {"camera-video"_L1, u"\ue04b"},
+ {"camera-web"_L1, u"\uf7a6"},
+ {"computer"_L1, u"\ue30a"},
+ {"drive-harddisk"_L1, u"\uf80e"},
+ {"drive-optical"_L1, u"\ue019"}, // same as media-optical
+ //{"drive-removable-media"_L1, u"\u"},
+ {"input-gaming"_L1, u"\uf5ee"},
+ {"input-keyboard"_L1, u"\ue312"},
+ {"input-mouse"_L1, u"\ue323"},
+ //{"input-tablet"_L1, u"\u"},
+ //{"media-flash"_L1, u"\u"},
+ //{"media-floppy"_L1, u"\u"},
+ {"media-optical"_L1, u"\ue019"},
+ //{"media-tape"_L1, u"\u"},
+ //{"modem"_L1, u"\u"},
+ //{"multimedia-player"_L1, u"\u"},
+ //{"network-wired"_L1, u"\u"},
+ {"network-wireless"_L1, u"\ue63e"},
+ //{"pda"_L1, u"\u"},
+ {"phone"_L1, u"\ue32c"},
+ {"printer"_L1, u"\ue8ad"},
+ {"scanner"_L1, u"\ue329"},
+ {"video-display"_L1, u"\uf06a"},
+ //{"emblem-default"_L1, u"\u"},
+ {"emblem-documents"_L1, u"\ue873"},
+ {"emblem-downloads"_L1, u"\uf090"},
+ {"emblem-favorite"_L1, u"\uf090"},
+ {"emblem-important"_L1, u"\ue645"},
+ {"emblem-mail"_L1, u"\ue158"},
+ {"emblem-photos"_L1, u"\ue413"},
+ //{"emblem-readonly"_L1, u"\u"},
+ {"emblem-shared"_L1, u"\ue413"},
+ //{"emblem-symbolic-link"_L1, u"\u"},
+ //{"emblem-synchronized"_L1, u"\u"},
+ {"emblem-system"_L1, u"\ue8b8"},
+ //{"emblem-unreadable"_L1, u"\u"},
+ {"folder"_L1, u"\ue2c7"},
+ //{"folder-remote"_L1, u"\u"},
+ {"network-server"_L1, u"\ue875"},
+ {"network-workgroup"_L1, u"\ue1a0"},
+ {"start-here"_L1, u"\ue089"},
+ {"user-bookmarks"_L1, u"\ue98b"},
+ {"user-desktop"_L1, u"\ue30a"},
+ {"user-home"_L1, u"\ue88a"},
+ {"user-trash"_L1, u"\ue872"},
+ {"appointment-missed"_L1, u"\ue615"},
+ {"appointment-soon"_L1, u"\uf540"},
+ {"audio-volume-high"_L1, u"\ue050"},
+ {"audio-volume-low"_L1, u"\ue04d"},
+ //{"audio-volume-medium"_L1, u"\u"},
+ {"audio-volume-muted"_L1, u"\ue04e"},
+ {"battery-caution"_L1, u"\ue19c"},
+ {"battery-low"_L1, u"\uf147"},
+ {"dialog-error"_L1, u"\ue000"},
+ {"dialog-information"_L1, u"\ue88e"},
+ {"dialog-password"_L1, u"\uf042"},
+ {"dialog-question"_L1, u"\ueb8b"},
+ {"dialog-warning"_L1, u"\ue002"},
+ {"folder-drag-accept"_L1, u"\ue9a3"},
+ {"folder-open"_L1, u"\ue2c8"},
+ {"folder-visiting"_L1, u"\ue8a7"},
+ {"image-loading"_L1, u"\ue41a"},
+ {"image-missing"_L1, u"\ue3ad"},
+ {"mail-attachment"_L1, u"\ue2bc"},
+ {"mail-unread"_L1, u"\uf18a"},
+ {"mail-read"_L1, u"\uf18c"},
+ //{"mail-replied"_L1, u"\u"},
+ //{"mail-signed"_L1, u"\u"},
+ //{"mail-signed-verified"_L1, u"\u"},
+ {"media-playlist-repeat"_L1, u"\ue040"},
+ {"media-playlist-shuffle"_L1, u"\ue043"},
+ {"network-error"_L1, u"\uead9"},
+ {"network-idle"_L1, u"\ue51f"},
+ {"network-offline"_L1, u"\uf239"},
+ {"network-receive"_L1, u"\ue2c0"},
+ {"network-transmit"_L1, u"\ue2c3"},
+ {"network-transmit-receive"_L1, u"\uea18"},
+ {"printer-error"_L1, u"\uf7a0"},
+ {"printer-printing"_L1, u"\uf7a1"},
+ {"security-high"_L1, u"\ue32a"},
+ {"security-medium"_L1, u"\ue9e0"},
+ {"security-low"_L1, u"\uf012"},
+ {"software-update-available"_L1, u"\ue923"},
+ {"software-update-urgent"_L1, u"\uf05a"},
+ {"sync-error"_L1, u"\ue629"},
+ {"sync-synchronizing"_L1, u"\ue627"},
+ //{"task-due"_L1, u"\u"},
+ //{"task-past-due"_L1, u"\u"},
+ {"user-available"_L1, u"\uf565"},
+ {"user-away"_L1, u"\ue510"},
+ //{"user-idle"_L1, u"\u"},
+ {"user-offline"_L1, u"\uf7b3"},
+ {"user-trash-full"_L1, u"\ue872"}, //delete
+ //{"user-trash-full"_L1, u"\ue92b"}, //delete_forever
+ {"weather-clear"_L1, u"\uf157"},
+ {"weather-clear-night"_L1, u"\uf159"},
+ {"weather-few-clouds"_L1, u"\uf172"},
+ {"weather-few-clouds-night"_L1, u"\uf174"},
+ {"weather-fog"_L1, u"\ue818"},
+ //{"weather-overcast"_L1, u"\u"},
+ {"weather-severe-alert"_L1, u"\ue002"}, //warning
+ //{"weather-severe-alert"_L1, u"\uebd3"},//severe_cold
+ {"weather-showers"_L1, u"\uf176"},
+ //{"weather-showers-scattered"_L1, u"\u"},
+ {"weather-snow"_L1, u"\ue80f"}, //snowing
+ //{"weather-snow"_L1, u"\ue2cd"}, //weather_snowy
+ //{"weather-snow"_L1, u"\ue810"},//cloudy_snowing
+ {"weather-storm"_L1, u"\uf070"},
+ };
+
+ const auto it = std::find_if(std::begin(glyphMap), std::end(glyphMap), [this](const auto &c){
+ return c.first == m_iconName;
+ });
+ return it != std::end(glyphMap) ? it->second.toString()
+ : (m_iconName.length() == 1 ? m_iconName : QString());
+}
+
+QAndroidPlatformIconEngine::QAndroidPlatformIconEngine(const QString &iconName)
+ : m_iconName(iconName)
+ , m_glyphs(glyphs())
+{
+ QString fontFamily;
+ // The MaterialIcons-*.ttf and MaterialSymbols* font files are available from
+ // https://github.com/google/material-design-icons/tree/master. If one of them is
+ // packaged as a resource with the application, then we use it. We prioritize
+ // a variable font.
+ const QStringList fontCandidates = {
+ "MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf",
+ "MaterialSymbolsRounded[FILL,GRAD,opsz,wght].ttf",
+ "MaterialSymbolsSharp[FILL,GRAD,opsz,wght].ttf",
+ "MaterialIcons-Regular.ttf",
+ "MaterialIconsOutlined-Regular.otf",
+ "MaterialIconsRound-Regular.otf",
+ "MaterialIconsSharp-Regular.otf",
+ "MaterialIconsTwoTone-Regular.otf",
+ };
+ for (const auto &fontCandidate : fontCandidates) {
+ fontFamily = FontProvider::fetchFont(u":/qt-project.org/icons/%1"_s.arg(fontCandidate));
+ if (!fontFamily.isEmpty())
+ break;
+ }
+
+ // Otherwise we try to download the Outlined version of Material Symbols
+ const QString key = qEnvironmentVariable("QT_GOOGLE_FONTS_KEY");
+ if (fontFamily.isEmpty() && !key.isEmpty())
+ fontFamily = FontProvider::fetchFont(u"key=%1&name=Material+Symbols+Outlined"_s.arg(key));
+
+ // last resort - use any Material Icons
+ if (fontFamily.isEmpty())
+ fontFamily = u"Material Icons"_s;
+ m_iconFont = QFont(fontFamily);
+}
+
+QAndroidPlatformIconEngine::~QAndroidPlatformIconEngine()
+{}
+
+QIconEngine *QAndroidPlatformIconEngine::clone() const
+{
+ return new QAndroidPlatformIconEngine(m_iconName);
+}
+
+QString QAndroidPlatformIconEngine::key() const
+{
+ return u"QAndroidPlatformIconEngine"_s;
+}
+
+QString QAndroidPlatformIconEngine::iconName()
+{
+ return m_iconName;
+}
+
+bool QAndroidPlatformIconEngine::isNull()
+{
+ if (m_glyphs.isEmpty())
+ return true;
+ const QChar c0 = m_glyphs.at(0);
+ const QFontMetrics fontMetrics(m_iconFont);
+ if (c0.category() == QChar::Other_Surrogate && m_glyphs.size() > 1)
+ return !fontMetrics.inFontUcs4(QChar::surrogateToUcs4(c0, m_glyphs.at(1)));
+ return !fontMetrics.inFont(c0);
+}
+
+QList<QSize> QAndroidPlatformIconEngine::availableSizes(QIcon::Mode, QIcon::State)
+{
+ return {{16, 16}, {24, 24}, {48, 48}, {128, 128}};
+}
+
+QSize QAndroidPlatformIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return QIconEngine::actualSize(size, mode, state);
+}
+
+QPixmap QAndroidPlatformIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
+{
+ return scaledPixmap(size, mode, state, 1.0);
+}
+
+QPixmap QAndroidPlatformIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
+{
+ const quint64 cacheKey = calculateCacheKey(mode, state);
+ if (cacheKey != m_cacheKey || m_pixmap.size() != size || m_pixmap.devicePixelRatio() != scale) {
+ m_pixmap = QPixmap(size * scale);
+ m_pixmap.fill(Qt::transparent);
+ m_pixmap.setDevicePixelRatio(scale);
+
+ QPainter painter(&m_pixmap);
+ paint(&painter, QRect(QPoint(), size), mode, state);
+
+ m_cacheKey = cacheKey;
+ }
+
+ return m_pixmap;
+}
+
+void QAndroidPlatformIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
+{
+ Q_UNUSED(state);
+
+ painter->save();
+ QFont renderFont(m_iconFont);
+ renderFont.setPixelSize(rect.height());
+ painter->setFont(renderFont);
+
+ QPalette palette;
+ switch (mode) {
+ case QIcon::Active:
+ painter->setPen(palette.color(QPalette::Active, QPalette::Text));
+ break;
+ case QIcon::Normal:
+ painter->setPen(palette.color(QPalette::Active, QPalette::Text));
+ break;
+ case QIcon::Disabled:
+ painter->setPen(palette.color(QPalette::Disabled, QPalette::Text));
+ break;
+ case QIcon::Selected:
+ painter->setPen(palette.color(QPalette::Active, QPalette::HighlightedText));
+ break;
+ }
+
+ painter->drawText(rect, Qt::AlignCenter, m_glyphs);
+ painter->restore();
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformiconengine.h b/src/plugins/platforms/android/qandroidplatformiconengine.h
new file mode 100644
index 0000000000..cac54481d9
--- /dev/null
+++ b/src/plugins/platforms/android/qandroidplatformiconengine.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QANDROIDPLATFORMICONENGINE_H
+#define QANDROIDPLATFORMICONENGINE_H
+
+#include <QtGui/qiconengine.h>
+#include <QtGui/qfont.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidPlatformIconEngine : public QIconEngine
+{
+public:
+ QAndroidPlatformIconEngine(const QString &iconName);
+ ~QAndroidPlatformIconEngine();
+ QIconEngine *clone() const override;
+ QString key() const override;
+ QString iconName() override;
+ bool isNull() override;
+
+ QList<QSize> availableSizes(QIcon::Mode, QIcon::State) override;
+ QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override;
+ QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override;
+ void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override;
+
+private:
+ static constexpr quint64 calculateCacheKey(QIcon::Mode mode, QIcon::State state)
+ {
+ return (quint64(mode) << 32) | state;
+ }
+ QString glyphs() const;
+
+ const QString m_iconName;
+ QFont m_iconFont;
+ const QString m_glyphs;
+ mutable QPixmap m_pixmap;
+ mutable quint64 m_cacheKey = {};
+};
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDPLATFORMICONENGINE_H
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp
index 7b4102fac6..6efd3fc631 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.cpp
+++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformintegration.h"
@@ -45,7 +9,6 @@
#include "qabstracteventdispatcher.h"
#include "qandroideventdispatcher.h"
#include "qandroidplatformaccessibility.h"
-#include "qandroidplatformbackingstore.h"
#include "qandroidplatformclipboard.h"
#include "qandroidplatformfontdatabase.h"
#include "qandroidplatformforeignwindow.h"
@@ -65,6 +28,7 @@
#include <QtGui/private/qeglpbuffer_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qoffscreensurface_p.h>
+#include <QtGui/private/qrhibackingstore_p.h>
#include <qpa/qplatformoffscreensurface.h>
#include <qpa/qplatformwindow.h>
#include <qpa/qwindowsysteminterface.h>
@@ -80,24 +44,54 @@
QT_BEGIN_NAMESPACE
-QSize QAndroidPlatformIntegration::m_defaultScreenSize = QSize(320, 455);
-QRect QAndroidPlatformIntegration::m_defaultAvailableGeometry = QRect(0, 0, 320, 455);
-QSize QAndroidPlatformIntegration::m_defaultPhysicalSize = QSize(50, 71);
+using namespace Qt::StringLiterals;
+
+Q_CONSTINIT QSize QAndroidPlatformIntegration::m_defaultScreenSize = QSize(320, 455);
+Q_CONSTINIT QRect QAndroidPlatformIntegration::m_defaultAvailableGeometry = QRect(0, 0, 320, 455);
+Q_CONSTINIT QSize QAndroidPlatformIntegration::m_defaultPhysicalSize = QSize(50, 71);
Qt::ScreenOrientation QAndroidPlatformIntegration::m_orientation = Qt::PrimaryOrientation;
Qt::ScreenOrientation QAndroidPlatformIntegration::m_nativeOrientation = Qt::PrimaryOrientation;
bool QAndroidPlatformIntegration::m_showPasswordEnabled = false;
-static bool m_running = false;
+
+Q_DECLARE_JNI_CLASS(QtNative, "org/qtproject/qt/android/QtNative")
+Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
+Q_DECLARE_JNI_CLASS(Display, "android/view/Display")
+
+Q_DECLARE_JNI_CLASS(List, "java/util/List")
+
+namespace {
+
+QAndroidPlatformScreen* createScreenForDisplayId(int displayId)
+{
+ const QJniObject display = QtJniTypes::QtDisplayManager::callStaticMethod<QtJniTypes::Display>(
+ "getDisplay", QtAndroidPrivate::context(), displayId);
+ if (!display.isValid())
+ return nullptr;
+ return new QAndroidPlatformScreen(display);
+}
+
+static bool isValidAndroidContextForRendering()
+{
+ return QtAndroid::isQtApplication() ? QtAndroidPrivate::activity().isValid()
+ : QtAndroidPrivate::context().isValid();
+}
+
+} // anonymous namespace
void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
{
if (resource=="JavaVM")
return QtAndroid::javaVM();
- if (resource == "QtActivity")
- return QtAndroid::activity();
- if (resource == "QtService")
- return QtAndroid::service();
+ if (resource == "QtActivity") {
+ extern Q_CORE_EXPORT jobject qt_androidActivity();
+ return qt_androidActivity();
+ }
+ if (resource == "QtService") {
+ extern Q_CORE_EXPORT jobject qt_androidService();
+ return qt_androidService();
+ }
if (resource == "AndroidStyleData") {
if (m_androidStyle) {
if (m_androidStyle->m_styleData.isEmpty())
@@ -143,6 +137,19 @@ void *QAndroidPlatformNativeInterface::nativeResourceForWindow(const QByteArray
return nullptr;
}
+void *QAndroidPlatformNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context)
+{
+ if (QEGLPlatformContext *platformContext = static_cast<QEGLPlatformContext *>(context->handle())) {
+ if (resource == "eglcontext")
+ return platformContext->eglContext();
+ else if (resource == "eglconfig")
+ return platformContext->eglConfig();
+ else if (resource == "egldisplay")
+ return platformContext->eglDisplay();
+ }
+ return nullptr;
+}
+
void QAndroidPlatformNativeInterface::customEvent(QEvent *event)
{
if (event->type() != QEvent::User)
@@ -152,21 +159,17 @@ void QAndroidPlatformNativeInterface::customEvent(QEvent *event)
QAndroidPlatformIntegration *api = static_cast<QAndroidPlatformIntegration *>(QGuiApplicationPrivate::platformIntegration());
QtAndroid::setAndroidPlatformIntegration(api);
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
// Android accessibility activation event might have been already received
api->accessibility()->setActive(QtAndroidAccessibility::isActive());
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
- if (!m_running) {
- m_running = true;
- QtAndroid::notifyQtAndroidPluginRunning(m_running);
- }
api->flushPendingUpdates();
}
QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &paramList)
: m_touchDevice(nullptr)
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
, m_accessibility(nullptr)
#endif
{
@@ -184,11 +187,32 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &para
if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API)))
qFatal("Could not bind GL_ES API");
- m_primaryScreen = new QAndroidPlatformScreen();
- QWindowSystemInterface::handleScreenAdded(m_primaryScreen);
- m_primaryScreen->setPhysicalSize(m_defaultPhysicalSize);
- m_primaryScreen->setSize(m_defaultScreenSize);
- m_primaryScreen->setAvailableGeometry(m_defaultAvailableGeometry);
+ using namespace QtJniTypes;
+ m_primaryDisplayId = Display::getStaticField<jint>("DEFAULT_DISPLAY");
+ const QJniObject nativeDisplaysList = QtDisplayManager::callStaticMethod<List>(
+ "getAvailableDisplays", QtAndroidPrivate::context());
+
+ const int numberOfAvailableDisplays = nativeDisplaysList.callMethod<jint>("size");
+ for (int i = 0; i < numberOfAvailableDisplays; ++i) {
+ const QJniObject display =
+ nativeDisplaysList.callObjectMethod<jobject, jint>("get", jint(i));
+ const int displayId = display.callMethod<jint>("getDisplayId");
+ const bool isPrimary = (m_primaryDisplayId == displayId);
+ auto screen = new QAndroidPlatformScreen(display);
+
+ if (isPrimary)
+ m_primaryScreen = screen;
+
+ QWindowSystemInterface::handleScreenAdded(screen, isPrimary);
+ m_screens[displayId] = screen;
+ }
+
+ if (numberOfAvailableDisplays == 0) {
+ // If no displays are found, add a dummy display
+ auto defaultScreen = new QAndroidPlatformScreen(QJniObject {});
+ m_primaryScreen = defaultScreen;
+ QWindowSystemInterface::handleScreenAdded(defaultScreen, true);
+ }
m_mainThread = QThread::currentThread();
@@ -201,13 +225,13 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &para
m_androidSystemLocale = new QAndroidSystemLocale;
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
m_accessibility = new QAndroidPlatformAccessibility();
-#endif // QT_NO_ACCESSIBILITY
+#endif // QT_CONFIG(accessibility)
- QJniObject javaActivity(QtAndroid::activity());
+ QJniObject javaActivity = QtAndroidPrivate::activity();
if (!javaActivity.isValid())
- javaActivity = QtAndroid::service();
+ javaActivity = QtAndroidPrivate::service();
if (javaActivity.isValid()) {
QJniObject resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;");
@@ -247,6 +271,10 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &para
maxTouchPoints,
0);
QWindowSystemInterface::registerInputDevice(m_touchDevice);
+
+ QWindowSystemInterface::registerInputDevice(
+ new QInputDevice("Virtual keyboard"_L1, 0, QInputDevice::DeviceType::Keyboard,
+ {}, qApp));
}
auto contentResolver = javaActivity.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;");
@@ -274,19 +302,19 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &para
static bool needsBasicRenderloopWorkaround()
{
static bool needsWorkaround =
- QtAndroid::deviceName().compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0
- || QtAndroid::deviceName().compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0
- || QtAndroid::deviceName().compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0;
+ QtAndroid::deviceName().compare("samsung SM-T211"_L1, Qt::CaseInsensitive) == 0
+ || QtAndroid::deviceName().compare("samsung SM-T210"_L1, Qt::CaseInsensitive) == 0
+ || QtAndroid::deviceName().compare("samsung SM-T215"_L1, Qt::CaseInsensitive) == 0;
return needsWorkaround;
}
void QAndroidPlatformIntegration::initialize()
{
- const QString icStr = QPlatformInputContextFactory::requested();
- if (icStr.isNull())
+ const auto icStrs = QPlatformInputContextFactory::requested();
+ if (icStrs.isEmpty())
m_inputContext.reset(new QAndroidInputContext);
else
- m_inputContext.reset(QPlatformInputContextFactory::create(icStr));
+ m_inputContext.reset(QPlatformInputContextFactory::create(icStrs));
}
bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
@@ -294,13 +322,19 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
switch (cap) {
case ApplicationState: return true;
case ThreadedPixmaps: return true;
- case NativeWidgets: return QtAndroid::activity();
- case OpenGL: return QtAndroid::activity();
- case ForeignWindows: return QtAndroid::activity();
- case ThreadedOpenGL: return !needsBasicRenderloopWorkaround() && QtAndroid::activity();
- case RasterGLSurface: return QtAndroid::activity();
+ case NativeWidgets: return QtAndroidPrivate::activity().isValid();
+ case OpenGL:
+ return isValidAndroidContextForRendering();
+ case ForeignWindows:
+ return isValidAndroidContextForRendering();
+ case ThreadedOpenGL:
+ return !needsBasicRenderloopWorkaround() && isValidAndroidContextForRendering();
+ case RasterGLSurface: return QtAndroidPrivate::activity().isValid();
case TopStackedNativeChildWindows: return false;
case MaximizeUsingFullscreenGeometry: return true;
+ // FIXME QTBUG-118849 - we do not implement grabWindow() anymore, calling it will return
+ // a null QPixmap also for raster windows - for OpenGL windows this was always true
+ case ScreenWindowGrabbing: return false;
default:
return QPlatformIntegration::hasCapability(cap);
}
@@ -308,15 +342,15 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(QWindow *window) const
{
- if (!QtAndroid::activity())
+ if (!QtAndroidPrivate::activity().isValid())
return nullptr;
- return new QAndroidPlatformBackingStore(window);
+ return new QRhiBackingStore(window);
}
QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
- if (!QtAndroid::activity())
+ if (!isValidAndroidContextForRendering())
return nullptr;
QSurfaceFormat format(context->format());
format.setAlphaBufferSize(8);
@@ -334,7 +368,7 @@ QOpenGLContext *QAndroidPlatformIntegration::createOpenGLContext(EGLContext cont
QPlatformOffscreenSurface *QAndroidPlatformIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const
{
- if (!QtAndroid::activity())
+ if (!QtAndroidPrivate::activity().isValid())
return nullptr;
QSurfaceFormat format(surface->requestedFormat());
@@ -348,7 +382,7 @@ QPlatformOffscreenSurface *QAndroidPlatformIntegration::createPlatformOffscreenS
QOffscreenSurface *QAndroidPlatformIntegration::createOffscreenSurface(ANativeWindow *nativeSurface) const
{
- if (!QtAndroid::activity() || !nativeSurface)
+ if (!QtAndroidPrivate::activity().isValid() || !nativeSurface)
return nullptr;
auto *surface = new QOffscreenSurface;
@@ -359,7 +393,7 @@ QOffscreenSurface *QAndroidPlatformIntegration::createOffscreenSurface(ANativeWi
QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const
{
- if (!QtAndroid::activity())
+ if (!isValidAndroidContextForRendering())
return nullptr;
#if QT_CONFIG(vulkan)
@@ -445,7 +479,7 @@ Qt::WindowState QAndroidPlatformIntegration::defaultWindowState(Qt::WindowFlags
return QPlatformIntegration::defaultWindowState(flags);
}
-static const QLatin1String androidThemeName("android");
+static const auto androidThemeName = "android"_L1;
QStringList QAndroidPlatformIntegration::themeNames() const
{
return QStringList(QString(androidThemeName));
@@ -454,19 +488,15 @@ QStringList QAndroidPlatformIntegration::themeNames() const
QPlatformTheme *QAndroidPlatformIntegration::createPlatformTheme(const QString &name) const
{
if (androidThemeName == name)
- return new QAndroidPlatformTheme(m_androidPlatformNativeInterface);
+ return QAndroidPlatformTheme::instance(m_androidPlatformNativeInterface);
return 0;
}
-void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int availableLeft,
- int availableTop,
- int availableWidth,
- int availableHeight,
- int physicalWidth,
- int physicalHeight,
- int screenWidth,
- int screenHeight)
+void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int availableLeft, int availableTop,
+ int availableWidth, int availableHeight,
+ int physicalWidth, int physicalHeight,
+ int screenWidth, int screenHeight)
{
m_defaultAvailableGeometry = QRect(availableLeft, availableTop,
availableWidth, availableHeight);
@@ -483,12 +513,13 @@ void QAndroidPlatformIntegration::setScreenOrientation(Qt::ScreenOrientation cur
void QAndroidPlatformIntegration::flushPendingUpdates()
{
- m_primaryScreen->setPhysicalSize(m_defaultPhysicalSize);
- m_primaryScreen->setSize(m_defaultScreenSize);
- m_primaryScreen->setAvailableGeometry(m_defaultAvailableGeometry);
+ if (m_primaryScreen) {
+ m_primaryScreen->setSizeParameters(m_defaultPhysicalSize, m_defaultScreenSize,
+ m_defaultAvailableGeometry);
+ }
}
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
QPlatformAccessibility *QAndroidPlatformIntegration::accessibility() const
{
return m_accessibility;
@@ -513,6 +544,78 @@ void QAndroidPlatformIntegration::setScreenSize(int width, int height)
QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height)));
}
+Qt::ColorScheme QAndroidPlatformIntegration::m_colorScheme = Qt::ColorScheme::Light;
+
+void QAndroidPlatformIntegration::updateColorScheme(Qt::ColorScheme colorScheme)
+{
+ if (m_colorScheme == colorScheme)
+ return;
+ m_colorScheme = colorScheme;
+
+ QMetaObject::invokeMethod(qGuiApp,
+ [] () { QAndroidPlatformTheme::instance()->updateColorScheme();});
+}
+
+void QAndroidPlatformIntegration::setScreenSizeParameters(const QSize &physicalSize,
+ const QSize &screenSize,
+ const QRect &availableGeometry)
+{
+ if (m_primaryScreen) {
+ QMetaObject::invokeMethod(m_primaryScreen, "setSizeParameters", Qt::AutoConnection,
+ Q_ARG(QSize, physicalSize), Q_ARG(QSize, screenSize),
+ Q_ARG(QRect, availableGeometry));
+ }
+}
+
+void QAndroidPlatformIntegration::setRefreshRate(qreal refreshRate)
+{
+ if (m_primaryScreen)
+ QMetaObject::invokeMethod(m_primaryScreen, "setRefreshRate", Qt::AutoConnection,
+ Q_ARG(qreal, refreshRate));
+}
+
+void QAndroidPlatformIntegration::handleScreenAdded(int displayId)
+{
+ auto result = m_screens.insert(displayId, nullptr);
+ if (result.first->second == nullptr) {
+ auto it = result.first;
+ it->second = createScreenForDisplayId(displayId);
+ if (it->second == nullptr)
+ return;
+ const bool isPrimary = (m_primaryDisplayId == displayId);
+ if (isPrimary)
+ m_primaryScreen = it->second;
+ QWindowSystemInterface::handleScreenAdded(it->second, isPrimary);
+ } else {
+ qWarning() << "Display with id" << displayId << "already exists.";
+ }
+}
+
+void QAndroidPlatformIntegration::handleScreenChanged(int displayId)
+{
+ auto it = m_screens.find(displayId);
+ if (it == m_screens.end() || it->second == nullptr) {
+ handleScreenAdded(displayId);
+ }
+ // We do not do anything more here as handling of change of
+ // rotation and refresh rate is done in QtActivityDelegate java class
+ // which calls QAndroidPlatformIntegration::setOrientation, and
+ // QAndroidPlatformIntegration::setRefreshRate accordingly.
+}
+
+void QAndroidPlatformIntegration::handleScreenRemoved(int displayId)
+{
+ auto it = m_screens.find(displayId);
+
+ if (it == m_screens.end())
+ return;
+
+ if (it->second != nullptr)
+ QWindowSystemInterface::handleScreenRemoved(it->second);
+
+ m_screens.erase(it);
+}
+
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.h b/src/plugins/platforms/android/qandroidplatformintegration.h
index d579bc29ae..b7bfb58d1d 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.h
+++ b/src/plugins/platforms/android/qandroidplatformintegration.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMINTERATION_H
#define QANDROIDPLATFORMINTERATION_H
@@ -49,6 +13,9 @@
#include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformopenglcontext.h>
#include <qpa/qplatformoffscreensurface.h>
+#include <qpa/qplatformtheme.h>
+#include <private/qflatmap_p.h>
+#include <QtCore/qvarlengtharray.h>
#include <EGL/egl.h>
#include <memory>
@@ -65,6 +32,7 @@ class QAndroidPlatformNativeInterface: public QPlatformNativeInterface
public:
void *nativeResourceForIntegration(const QByteArray &resource) override;
void *nativeResourceForWindow(const QByteArray &resource, QWindow *window) override;
+ void *nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) override;
std::shared_ptr<AndroidStyle> m_androidStyle;
protected:
@@ -98,10 +66,21 @@ public:
void setAvailableGeometry(const QRect &availableGeometry);
void setPhysicalSize(int width, int height);
void setScreenSize(int width, int height);
+ // The 3 methods above were replaced by a new one, so that we could have
+ // a better control over "geometry changed" event handling. Technically
+ // they are no longer used and can be removed. Not doing it now, because
+ // I'm not sure if it might be helpful to have them or not.
+ void setScreenSizeParameters(const QSize &physicalSize, const QSize &screenSize,
+ const QRect &availableGeometry);
+ void setRefreshRate(qreal refreshRate);
bool isVirtualDesktop() { return true; }
QPlatformFontDatabase *fontDatabase() const override;
+ void handleScreenAdded(int displayId);
+ void handleScreenChanged(int displayId);
+ void handleScreenRemoved(int displayId);
+
#ifndef QT_NO_CLIPBOARD
QPlatformClipboard *clipboard() const override;
#endif
@@ -110,7 +89,7 @@ public:
QPlatformNativeInterface *nativeInterface() const override;
QPlatformServices *services() const override;
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
virtual QPlatformAccessibility *accessibility() const override;
#endif
@@ -120,14 +99,9 @@ public:
QStringList themeNames() const override;
QPlatformTheme *createPlatformTheme(const QString &name) const override;
- static void setDefaultDisplayMetrics(int availableLeft,
- int availableTop,
- int availableWidth,
- int availableHeight,
- int physicalWidth,
- int physicalHeight,
- int screenWidth,
- int screenHeight);
+ static void setDefaultDisplayMetrics(int availableLeft, int availableTop, int availableWidth,
+ int availableHeight, int physicalWidth, int physicalHeight,
+ int screenWidth, int screenHeight);
static void setScreenOrientation(Qt::ScreenOrientation currentOrientation,
Qt::ScreenOrientation nativeOrientation);
@@ -136,6 +110,8 @@ public:
void flushPendingUpdates();
+ static void updateColorScheme(Qt::ColorScheme colorScheme);
+ static Qt::ColorScheme colorScheme() { return m_colorScheme; }
#if QT_CONFIG(vulkan)
QPlatformVulkanInstance *createPlatformVulkanInstance(QVulkanInstance *instance) const override;
#endif
@@ -148,6 +124,8 @@ private:
QThread *m_mainThread;
+ static Qt::ColorScheme m_colorScheme;
+
static QRect m_defaultAvailableGeometry;
static QSize m_defaultPhysicalSize;
static QSize m_defaultScreenSize;
@@ -160,12 +138,23 @@ private:
QAndroidPlatformNativeInterface *m_androidPlatformNativeInterface;
QAndroidPlatformServices *m_androidPlatformServices;
+ // Handling the multiple screens connected. Every display is identified
+ // with an unique (autoincremented) displayID. The values of this ID will
+ // not repeat during the OS runtime. We use this value as the key in the
+ // storage of screens.
+ QFlatMap<int, QAndroidPlatformScreen *, std::less<int>
+ , QVarLengthArray<int, 10>
+ , QVarLengthArray<QAndroidPlatformScreen *, 10> > m_screens;
+ // ID of the primary display, in documentation it is said to be always 0,
+ // but nevertheless it is retrieved
+ int m_primaryDisplayId = 0;
+
#ifndef QT_NO_CLIPBOARD
QPlatformClipboard *m_androidPlatformClipboard;
#endif
QAndroidSystemLocale *m_androidSystemLocale;
-#ifndef QT_NO_ACCESSIBILITY
+#if QT_CONFIG(accessibility)
mutable QPlatformAccessibility *m_accessibility;
#endif
diff --git a/src/plugins/platforms/android/qandroidplatformmenu.cpp b/src/plugins/platforms/android/qandroidplatformmenu.cpp
index 8d79331f03..e59fd2089d 100644
--- a/src/plugins/platforms/android/qandroidplatformmenu.cpp
+++ b/src/plugins/platforms/android/qandroidplatformmenu.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidjnimenu.h"
#include "qandroidplatformmenu.h"
@@ -155,7 +119,7 @@ void QAndroidPlatformMenu::showPopup(const QWindow *parentWindow, const QRect &t
Q_UNUSED(parentWindow);
Q_UNUSED(item);
setVisible(true);
- QtAndroidMenu::showContextMenu(this, targetRect, QJniEnvironment().jniEnv());
+ QtAndroidMenu::showContextMenu(this, targetRect);
}
QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const
diff --git a/src/plugins/platforms/android/qandroidplatformmenu.h b/src/plugins/platforms/android/qandroidplatformmenu.h
index e745360122..4329b03e58 100644
--- a/src/plugins/platforms/android/qandroidplatformmenu.h
+++ b/src/plugins/platforms/android/qandroidplatformmenu.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMMENU_H
#define QANDROIDPLATFORMMENU_H
diff --git a/src/plugins/platforms/android/qandroidplatformmenubar.cpp b/src/plugins/platforms/android/qandroidplatformmenubar.cpp
index 7c6299b4b7..f3d99b9d90 100644
--- a/src/plugins/platforms/android/qandroidplatformmenubar.cpp
+++ b/src/plugins/platforms/android/qandroidplatformmenubar.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformmenubar.h"
#include "qandroidplatformmenu.h"
diff --git a/src/plugins/platforms/android/qandroidplatformmenubar.h b/src/plugins/platforms/android/qandroidplatformmenubar.h
index 449c39ca52..0d2ee1b8b6 100644
--- a/src/plugins/platforms/android/qandroidplatformmenubar.h
+++ b/src/plugins/platforms/android/qandroidplatformmenubar.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMMENUBAR_H
#define QANDROIDPLATFORMMENUBAR_H
diff --git a/src/plugins/platforms/android/qandroidplatformmenuitem.cpp b/src/plugins/platforms/android/qandroidplatformmenuitem.cpp
index 8c142603df..3e3facef3e 100644
--- a/src/plugins/platforms/android/qandroidplatformmenuitem.cpp
+++ b/src/plugins/platforms/android/qandroidplatformmenuitem.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformmenuitem.h"
#include "qandroidplatformmenu.h"
diff --git a/src/plugins/platforms/android/qandroidplatformmenuitem.h b/src/plugins/platforms/android/qandroidplatformmenuitem.h
index b8782f995d..e201a5d91a 100644
--- a/src/plugins/platforms/android/qandroidplatformmenuitem.h
+++ b/src/plugins/platforms/android/qandroidplatformmenuitem.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMMENUITEM_H
#define QANDROIDPLATFORMMENUITEM_H
diff --git a/src/plugins/platforms/android/qandroidplatformoffscreensurface.cpp b/src/plugins/platforms/android/qandroidplatformoffscreensurface.cpp
index 9255ef4446..5237218d62 100644
--- a/src/plugins/platforms/android/qandroidplatformoffscreensurface.cpp
+++ b/src/plugins/platforms/android/qandroidplatformoffscreensurface.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformoffscreensurface.h"
diff --git a/src/plugins/platforms/android/qandroidplatformoffscreensurface.h b/src/plugins/platforms/android/qandroidplatformoffscreensurface.h
index e57ab2afad..a48a43ca98 100644
--- a/src/plugins/platforms/android/qandroidplatformoffscreensurface.h
+++ b/src/plugins/platforms/android/qandroidplatformoffscreensurface.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMOFFSCREENSURFACETEXTURE_H
#define QANDROIDPLATFORMOFFSCREENSURFACETEXTURE_H
diff --git a/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp b/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp
index c51fbaf1d3..faccf8b117 100644
--- a/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp
+++ b/src/plugins/platforms/android/qandroidplatformopenglcontext.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformopenglcontext.h"
#include "qandroidplatformopenglwindow.h"
diff --git a/src/plugins/platforms/android/qandroidplatformopenglcontext.h b/src/plugins/platforms/android/qandroidplatformopenglcontext.h
index b3dc5b297b..448f7d0d75 100644
--- a/src/plugins/platforms/android/qandroidplatformopenglcontext.h
+++ b/src/plugins/platforms/android/qandroidplatformopenglcontext.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMOPENGLCONTEXT_H
#define QANDROIDPLATFORMOPENGLCONTEXT_H
diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp
index 1c3fd9c80a..13d14eb391 100644
--- a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp
+++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformopenglwindow.h"
@@ -60,41 +24,19 @@ QT_BEGIN_NAMESPACE
QAndroidPlatformOpenGLWindow::QAndroidPlatformOpenGLWindow(QWindow *window, EGLDisplay display)
:QAndroidPlatformWindow(window), m_eglDisplay(display)
{
+ if (window->surfaceType() == QSurface::RasterSurface)
+ window->setSurfaceType(QSurface::OpenGLSurface);
}
QAndroidPlatformOpenGLWindow::~QAndroidPlatformOpenGLWindow()
{
m_surfaceWaitCondition.wakeOne();
lockSurface();
- if (m_nativeSurfaceId != -1)
- QtAndroid::destroySurface(m_nativeSurfaceId);
+ destroySurface();
clearEgl();
unlockSurface();
}
-void QAndroidPlatformOpenGLWindow::repaint(const QRegion &region)
-{
- // This is only for real raster top-level windows. Stop in all other cases.
- if ((window()->surfaceType() == QSurface::RasterGLSurface && qt_window_private(window())->compositing)
- || window()->surfaceType() == QSurface::OpenGLSurface
- || QAndroidPlatformWindow::parent())
- return;
-
- QRect currentGeometry = geometry();
-
- QRect dirtyClient = region.boundingRect();
- QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(),
- currentGeometry.top() + dirtyClient.top(),
- dirtyClient.width(),
- dirtyClient.height());
- QRect mOldGeometryLocal = m_oldGeometry;
- m_oldGeometry = currentGeometry;
- // If this is a move, redraw the previous location
- if (mOldGeometryLocal != currentGeometry)
- platformScreen()->setDirty(mOldGeometryLocal);
- platformScreen()->setDirty(dirtyRegion);
-}
-
void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
{
if (rect == geometry())
@@ -103,8 +45,9 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
m_oldGeometry = geometry();
QAndroidPlatformWindow::setGeometry(rect);
- if (m_nativeSurfaceId != -1)
- QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
+
+
+ setNativeGeometry(rect);
QRect availableGeometry = screen()->availableGeometry();
if (rect.width() > 0
@@ -113,25 +56,23 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
&& availableGeometry.height() > 0) {
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
-
- if (rect.topLeft() != m_oldGeometry.topLeft())
- repaint(QRegion(rect));
}
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
{
- if (QAndroidEventDispatcherStopper::stopped() || QGuiApplication::applicationState() == Qt::ApplicationSuspended)
+ if (QAndroidEventDispatcherStopper::stopped() ||
+ QGuiApplication::applicationState() == Qt::ApplicationSuspended) {
return m_eglSurface;
+ }
QMutexLocker lock(&m_surfaceMutex);
- if (m_nativeSurfaceId == -1) {
+ if (!m_surfaceCreated) {
AndroidDeadlockProtector protector;
if (!protector.acquire())
return m_eglSurface;
- const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
- m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
+ createSurface();
m_surfaceWaitCondition.wait(&m_surfaceMutex);
}
@@ -146,7 +87,7 @@ EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
bool QAndroidPlatformOpenGLWindow::checkNativeSurface(EGLConfig config)
{
QMutexLocker lock(&m_surfaceMutex);
- if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid())
+ if (!m_surfaceCreated || !m_androidSurfaceObject.isValid())
return false; // makeCurrent is NOT needed.
createEgl(config);
@@ -163,10 +104,7 @@ void QAndroidPlatformOpenGLWindow::applicationStateChanged(Qt::ApplicationState
QAndroidPlatformWindow::applicationStateChanged(state);
if (state <= Qt::ApplicationHidden) {
lockSurface();
- if (m_nativeSurfaceId != -1) {
- QtAndroid::destroySurface(m_nativeSurfaceId);
- m_nativeSurfaceId = -1;
- }
+ destroySurface();
clearEgl();
unlockSurface();
}
@@ -209,24 +147,4 @@ void QAndroidPlatformOpenGLWindow::clearEgl()
}
}
-void QAndroidPlatformOpenGLWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
-{
- Q_UNUSED(jniEnv);
- Q_UNUSED(w);
- Q_UNUSED(h);
-
- lockSurface();
- m_androidSurfaceObject = surface;
- if (surface) // wait until we have a valid surface to draw into
- m_surfaceWaitCondition.wakeOne();
- unlockSurface();
-
- if (surface) {
- // repaint the window, when we have a valid surface
- QRect availableGeometry = screen()->availableGeometry();
- if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
- QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
- }
-}
-
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.h b/src/plugins/platforms/android/qandroidplatformopenglwindow.h
index b8f1a5f9fc..c1ae57fe85 100644
--- a/src/plugins/platforms/android/qandroidplatformopenglwindow.h
+++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.h
@@ -1,47 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMOPENGLWINDOW_H
#define QANDROIDPLATFORMOPENGLWINDOW_H
-#include "androidsurfaceclient.h"
#include "qandroidplatformwindow.h"
#include <QWaitCondition>
@@ -52,7 +15,7 @@
QT_BEGIN_NAMESPACE
-class QAndroidPlatformOpenGLWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient
+class QAndroidPlatformOpenGLWindow : public QAndroidPlatformWindow
{
public:
explicit QAndroidPlatformOpenGLWindow(QWindow *window, EGLDisplay display);
@@ -66,10 +29,7 @@ public:
void applicationStateChanged(Qt::ApplicationState) override;
- void repaint(const QRegion &region) override;
-
protected:
- void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) override;
void createEgl(EGLConfig config);
void clearEgl();
@@ -78,9 +38,6 @@ private:
EGLSurface m_eglSurface = EGL_NO_SURFACE;
EGLNativeWindowType m_nativeWindow = nullptr;
- int m_nativeSurfaceId = -1;
- QJniObject m_androidSurfaceObject;
- QWaitCondition m_surfaceWaitCondition;
QSurfaceFormat m_format;
QRect m_oldGeometry;
};
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp
index 6a83827262..9e20b7ac4b 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.cpp
+++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QDebug>
#include <QTime>
@@ -44,7 +8,6 @@
#include <qpa/qwindowsysteminterface.h>
#include "qandroidplatformscreen.h"
-#include "qandroidplatformbackingstore.h"
#include "qandroidplatformintegration.h"
#include "qandroidplatformwindow.h"
#include "androidjnimain.h"
@@ -55,10 +18,11 @@
#include <android/native_window_jni.h>
#include <qguiapplication.h>
+#include <QtCore/QJniObject>
+#include <QtCore/QJniEnvironment>
#include <QtGui/QGuiApplication>
#include <QtGui/QWindow>
#include <QtGui/private/qwindow_p.h>
-
#include <vector>
QT_BEGIN_NAMESPACE
@@ -87,11 +51,22 @@ private:
# define PROFILE_SCOPE
#endif
-QAndroidPlatformScreen::QAndroidPlatformScreen()
+Q_DECLARE_JNI_CLASS(Display, "android/view/Display")
+Q_DECLARE_JNI_CLASS(DisplayMetrics, "android/util/DisplayMetrics")
+Q_DECLARE_JNI_CLASS(Resources, "android/content/res/Resources")
+Q_DECLARE_JNI_CLASS(Size, "android/util/Size")
+Q_DECLARE_JNI_CLASS(QtNative, "org/qtproject/qt/android/QtNative")
+Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
+
+Q_DECLARE_JNI_CLASS(DisplayMode, "android/view/Display$Mode")
+
+QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
: QObject(), QPlatformScreen()
{
m_availableGeometry = QAndroidPlatformIntegration::m_defaultAvailableGeometry;
m_size = QAndroidPlatformIntegration::m_defaultScreenSize;
+ m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
+
// Raster only apps should set QT_ANDROID_RASTER_IMAGE_DEPTH to 16
// is way much faster than 32
if (qEnvironmentVariableIntValue("QT_ANDROID_RASTER_IMAGE_DEPTH") == 16) {
@@ -101,29 +76,72 @@ QAndroidPlatformScreen::QAndroidPlatformScreen()
m_format = QImage::Format_ARGB32_Premultiplied;
m_depth = 32;
}
- m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
- connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
+
+ connect(qGuiApp, &QGuiApplication::applicationStateChanged, this,
+ &QAndroidPlatformScreen::applicationStateChanged);
+
+ if (!displayObject.isValid())
+ return;
+
+ m_name = displayObject.callObjectMethod<jstring>("getName").toString();
+ m_refreshRate = displayObject.callMethod<jfloat>("getRefreshRate");
+ m_displayId = displayObject.callMethod<jint>("getDisplayId");
+
+ const QJniObject context = QNativeInterface::QAndroidApplication::context();
+ const auto displayContext = context.callMethod<QtJniTypes::Context>("createDisplayContext",
+ displayObject.object<QtJniTypes::Display>());
+
+ const auto sizeObj = QtJniTypes::QtDisplayManager::callStaticMethod<QtJniTypes::Size>(
+ "getDisplaySize", displayContext,
+ displayObject.object<QtJniTypes::Display>());
+ m_size = QSize(sizeObj.callMethod<int>("getWidth"), sizeObj.callMethod<int>("getHeight"));
+
+ const auto resources = displayContext.callMethod<QtJniTypes::Resources>("getResources");
+ const auto metrics = resources.callMethod<QtJniTypes::DisplayMetrics>("getDisplayMetrics");
+ const float xdpi = metrics.getField<float>("xdpi");
+ const float ydpi = metrics.getField<float>("ydpi");
+
+ // Potentially densityDpi could be used instead of xpdi/ydpi to do the calculation,
+ // but the results are not consistent with devices specs.
+ // (https://issuetracker.google.com/issues/194120500)
+ m_physicalSize.setWidth(qRound(m_size.width() / xdpi * 25.4));
+ m_physicalSize.setHeight(qRound(m_size.height() / ydpi * 25.4));
+
+ if (QNativeInterface::QAndroidApplication::sdkVersion() >= 23) {
+ const QJniObject currentMode = displayObject.callObjectMethod<QtJniTypes::DisplayMode>("getMode");
+ m_currentMode = currentMode.callMethod<jint>("getModeId");
+
+ const QJniObject supportedModes = displayObject.callObjectMethod<QtJniTypes::DisplayMode[]>(
+ "getSupportedModes");
+ const auto modeArray = jobjectArray(supportedModes.object());
+
+ QJniEnvironment env;
+ const auto size = env->GetArrayLength(modeArray);
+ for (jsize i = 0; i < size; ++i) {
+ const auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modeArray, i));
+ m_modes << QPlatformScreen::Mode {
+ .size = QSize { mode.callMethod<jint>("getPhysicalWidth"),
+ mode.callMethod<jint>("getPhysicalHeight") },
+ .refreshRate = mode.callMethod<jfloat>("getRefreshRate")
+ };
+ }
+ }
}
QAndroidPlatformScreen::~QAndroidPlatformScreen()
{
- if (m_id != -1) {
- QtAndroid::destroySurface(m_id);
- m_surfaceWaitCondition.wakeOne();
- releaseSurface();
- }
}
-QWindow *QAndroidPlatformScreen::topWindow() const
+QWindow *QAndroidPlatformScreen::topVisibleWindow() const
{
for (QAndroidPlatformWindow *w : m_windowStack) {
- if (w->window()->type() == Qt::Window ||
- w->window()->type() == Qt::Popup ||
- w->window()->type() == Qt::Dialog) {
+ Qt::WindowType type = w->window()->type();
+ if (w->window()->isVisible() &&
+ (type == Qt::Window || type == Qt::Popup || type == Qt::Dialog)) {
return w->window();
}
}
- return 0;
+ return nullptr;
}
QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const
@@ -135,116 +153,105 @@ QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const
return 0;
}
-bool QAndroidPlatformScreen::event(QEvent *event)
-{
- if (event->type() == QEvent::UpdateRequest) {
- doRedraw();
- m_updatePending = false;
- return true;
- }
- return QObject::event(event);
-}
-
void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
{
if (window->parent() && window->isRaster())
return;
- Q_ASSERT(!m_windowStack.contains(window));
+ if (m_windowStack.contains(window))
+ return;
+
m_windowStack.prepend(window);
- if (window->isRaster()) {
- m_rasterSurfaces.ref();
- setDirty(window->geometry());
- }
+ QtAndroid::qtActivityDelegate().callMethod<void>("addTopLevelWindow", window->nativeWindow());
- QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
- topWindowChanged(w);
+ if (window->window()->isVisible())
+ topVisibleWindowChanged();
}
void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
{
- if (window->parent() && window->isRaster())
- return;
-
-
- Q_ASSERT(m_windowStack.contains(window));
m_windowStack.removeOne(window);
- Q_ASSERT(!m_windowStack.contains(window));
- if (window->isRaster()) {
- m_rasterSurfaces.deref();
- setDirty(window->geometry());
- }
+ if (m_windowStack.contains(window))
+ qWarning() << "Failed to remove window";
+
+ QtAndroid::qtActivityDelegate().callMethod<void>("removeTopLevelWindow", window->nativeViewId());
- QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
- topWindowChanged(w);
+ topVisibleWindowChanged();
}
void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
{
- if (window->parent() && window->isRaster())
- return;
-
int index = m_windowStack.indexOf(window);
- if (index <= 0)
+ if (index < 0)
return;
- m_windowStack.move(index, 0);
- if (window->isRaster()) {
- setDirty(window->geometry());
+ if (index > 0) {
+ m_windowStack.move(index, 0);
+ QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToFront", window->nativeViewId());
}
- QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
- topWindowChanged(w);
+ topVisibleWindowChanged();
}
void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
{
- if (window->parent() && window->isRaster())
- return;
-
int index = m_windowStack.indexOf(window);
if (index == -1 || index == (m_windowStack.size() - 1))
return;
m_windowStack.move(index, m_windowStack.size() - 1);
- if (window->isRaster()) {
- setDirty(window->geometry());
- }
- QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w);
- topWindowChanged(w);
+ QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToBack", window->nativeViewId());
+
+ topVisibleWindowChanged();
+}
+
+void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
+{
+ m_physicalSize = size;
+}
+
+void QAndroidPlatformScreen::setSize(const QSize &size)
+{
+ m_size = size;
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
}
-void QAndroidPlatformScreen::scheduleUpdate()
+void QAndroidPlatformScreen::setSizeParameters(const QSize &physicalSize, const QSize &size,
+ const QRect &availableGeometry)
{
- if (!m_updatePending) {
- m_updatePending = true;
- QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+ // The goal of this method is to set all geometry-related parameters
+ // at the same time and generate only one screen geometry change event.
+ m_physicalSize = physicalSize;
+ m_size = size;
+ // If available geometry has changed, the event will be handled in
+ // setAvailableGeometry. Otherwise we need to explicitly handle it to
+ // retain the behavior, because setSize() does the handling unconditionally.
+ if (m_availableGeometry != availableGeometry) {
+ setAvailableGeometry(availableGeometry);
+ } else {
+ QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(),
+ this->availableGeometry());
}
}
-void QAndroidPlatformScreen::setDirty(const QRect &rect)
+int QAndroidPlatformScreen::displayId() const
{
- QRect intersection = rect.intersected(m_availableGeometry);
- m_dirtyRect |= intersection;
- scheduleUpdate();
+ return m_displayId;
}
-void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
+void QAndroidPlatformScreen::setRefreshRate(qreal refreshRate)
{
- m_physicalSize = size;
+ if (refreshRate == m_refreshRate)
+ return;
+ m_refreshRate = refreshRate;
+ QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), refreshRate);
}
-void QAndroidPlatformScreen::setSize(const QSize &size)
+void QAndroidPlatformScreen::setOrientation(Qt::ScreenOrientation orientation)
{
- m_size = size;
- QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
+ QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), orientation);
}
void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
{
- QMutexLocker lock(&m_surfaceMutex);
if (m_availableGeometry == rect)
return;
@@ -265,170 +272,31 @@ void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
}
}
}
-
- if (m_id != -1) {
- releaseSurface();
- QtAndroid::setSurfaceGeometry(m_id, rect);
- }
}
void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state)
{
- for (QAndroidPlatformWindow *w : qAsConst(m_windowStack))
+ for (QAndroidPlatformWindow *w : std::as_const(m_windowStack))
w->applicationStateChanged(state);
-
- if (state <= Qt::ApplicationHidden) {
- lockSurface();
- QtAndroid::destroySurface(m_id);
- m_id = -1;
- releaseSurface();
- unlockSurface();
- }
}
-void QAndroidPlatformScreen::topWindowChanged(QWindow *w)
+void QAndroidPlatformScreen::topVisibleWindowChanged()
{
+ QWindow *w = topVisibleWindow();
+ QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
QtAndroidMenu::setActiveTopLevelWindow(w);
-
- if (w != 0) {
+ if (w && w->handle()) {
QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle());
- if (platformWindow != 0)
+ if (platformWindow)
platformWindow->updateSystemUiVisibility();
}
}
-int QAndroidPlatformScreen::rasterSurfaces()
-{
- return m_rasterSurfaces;
-}
-
-void QAndroidPlatformScreen::doRedraw(QImage* screenGrabImage)
-{
- PROFILE_SCOPE;
- if (!QtAndroid::activity())
- return;
-
- if (m_dirtyRect.isEmpty())
- return;
-
- // Stop if there are no visible raster windows. If we only have RasterGLSurface
- // windows that have renderToTexture children (i.e. they need the OpenGL path) then
- // we do not need an overlay surface.
- bool hasVisibleRasterWindows = false;
- for (QAndroidPlatformWindow *window : qAsConst(m_windowStack)) {
- if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) {
- hasVisibleRasterWindows = true;
- break;
- }
- }
- if (!hasVisibleRasterWindows) {
- lockSurface();
- if (m_id != -1) {
- QtAndroid::destroySurface(m_id);
- releaseSurface();
- m_id = -1;
- }
- unlockSurface();
- return;
- }
- QMutexLocker lock(&m_surfaceMutex);
- if (m_id == -1 && m_rasterSurfaces) {
- m_id = QtAndroid::createSurface(this, geometry(), true, m_depth);
- AndroidDeadlockProtector protector;
- if (!protector.acquire())
- return;
- m_surfaceWaitCondition.wait(&m_surfaceMutex);
- }
-
- if (!m_nativeSurface)
- return;
-
- ANativeWindow_Buffer nativeWindowBuffer;
- ARect nativeWindowRect;
- nativeWindowRect.top = m_dirtyRect.top();
- nativeWindowRect.left = m_dirtyRect.left();
- nativeWindowRect.bottom = m_dirtyRect.bottom() + 1; // for some reason that I don't understand the QRect bottom needs to +1 to be the same with ARect bottom
- nativeWindowRect.right = m_dirtyRect.right() + 1; // same for the right
-
- int ret;
- if ((ret = ANativeWindow_lock(m_nativeSurface, &nativeWindowBuffer, &nativeWindowRect)) < 0) {
- qWarning() << "ANativeWindow_lock() failed! error=" << ret;
- return;
- }
-
- int bpp = 4;
- if (nativeWindowBuffer.format == WINDOW_FORMAT_RGB_565) {
- bpp = 2;
- m_pixelFormat = QImage::Format_RGB16;
- }
-
- QImage screenImage(reinterpret_cast<uchar *>(nativeWindowBuffer.bits)
- , nativeWindowBuffer.width, nativeWindowBuffer.height
- , nativeWindowBuffer.stride * bpp , m_pixelFormat);
-
- QPainter compositePainter(&screenImage);
- compositePainter.setCompositionMode(QPainter::CompositionMode_Source);
-
- QRegion visibleRegion(m_dirtyRect);
- for (QAndroidPlatformWindow *window : qAsConst(m_windowStack)) {
- if (!window->window()->isVisible()
- || qt_window_private(window->window())->compositing
- || !window->isRaster())
- continue;
-
- for (const QRect &rect : std::vector<QRect>(visibleRegion.begin(), visibleRegion.end())) {
- QRect targetRect = window->geometry();
- targetRect &= rect;
-
- if (targetRect.isNull())
- continue;
-
- visibleRegion -= targetRect;
- QRect windowRect = targetRect.translated(-window->geometry().topLeft());
- QAndroidPlatformBackingStore *backingStore = static_cast<QAndroidPlatformWindow *>(window)->backingStore();
- if (backingStore)
- compositePainter.drawImage(targetRect.topLeft(), backingStore->toImage(), windowRect);
- }
- }
-
- for (const QRect &rect : visibleRegion)
- compositePainter.fillRect(rect, QColor(Qt::transparent));
-
- ret = ANativeWindow_unlockAndPost(m_nativeSurface);
- if (ret >= 0)
- m_dirtyRect = QRect();
-
- if (screenGrabImage) {
- if (screenGrabImage->size() != screenImage.size()) {
- uchar* bytes = static_cast<uchar*>(malloc(screenImage.height() * screenImage.bytesPerLine()));
- *screenGrabImage = QImage(bytes, screenImage.width(), screenImage.height(),
- screenImage.bytesPerLine(), m_pixelFormat,
- [](void* ptr){ if (ptr) free (ptr);});
- }
- memcpy(screenGrabImage->bits(),
- screenImage.bits(),
- screenImage.bytesPerLine() * screenImage.height());
- }
- m_repaintOccurred = true;
-}
-
-QPixmap QAndroidPlatformScreen::doScreenShot(QRect grabRect)
-{
- if (!m_repaintOccurred)
- return QPixmap::fromImage(m_lastScreenshot.copy(grabRect));
- QRect tmp = m_dirtyRect;
- m_dirtyRect = geometry();
- doRedraw(&m_lastScreenshot);
- m_dirtyRect = tmp;
- m_repaintOccurred = false;
- return QPixmap::fromImage(m_lastScreenshot.copy(grabRect));
-}
-
static const int androidLogicalDpi = 72;
QDpi QAndroidPlatformScreen::logicalDpi() const
{
- qreal lDpi = QtAndroid::scaledDensity() * androidLogicalDpi;
+ qreal lDpi = QtAndroid::pixelDensity() * androidLogicalDpi;
return QDpi(lDpi, lDpi);
}
@@ -446,67 +314,4 @@ Qt::ScreenOrientation QAndroidPlatformScreen::nativeOrientation() const
{
return QAndroidPlatformIntegration::m_nativeOrientation;
}
-
-void QAndroidPlatformScreen::surfaceChanged(JNIEnv *env, jobject surface, int w, int h)
-{
- lockSurface();
- if (surface && w > 0 && h > 0) {
- releaseSurface();
- m_nativeSurface = ANativeWindow_fromSurface(env, surface);
- QMetaObject::invokeMethod(this, "setDirty", Qt::QueuedConnection, Q_ARG(QRect, QRect(0, 0, w, h)));
- } else {
- releaseSurface();
- }
- unlockSurface();
- m_surfaceWaitCondition.wakeOne();
-}
-
-void QAndroidPlatformScreen::releaseSurface()
-{
- if (m_nativeSurface) {
- ANativeWindow_release(m_nativeSurface);
- m_nativeSurface = 0;
- }
-}
-
-/*!
- This function is called when Qt needs to be able to grab the content of a window.
-
- Returns the content of the window specified with the WId handle within the boundaries of
- QRect(x, y, width, height).
-*/
-QPixmap QAndroidPlatformScreen::grabWindow(WId window, int x, int y, int width, int height) const
-{
- QRectF screenshotRect(x, y, width, height);
- QWindow* wnd = 0;
- if (window)
- {
- const auto windowList = qApp->allWindows();
- for (QWindow *w : windowList)
- if (w->winId() == window) {
- wnd = w;
- break;
- }
- }
- if (wnd) {
- const qreal factor = logicalDpi().first / androidLogicalDpi; //HighDPI factor;
- QRectF wndRect = wnd->geometry();
- if (wnd->parent())
- wndRect.moveTopLeft(wnd->parent()->mapToGlobal(wndRect.topLeft().toPoint()));
- if (!qFuzzyCompare(factor, 1))
- wndRect = QRectF(wndRect.left() * factor, wndRect.top() * factor,
- wndRect.width() * factor, wndRect.height() * factor);
-
- if (!screenshotRect.isEmpty()) {
- screenshotRect.moveTopLeft(wndRect.topLeft() + screenshotRect.topLeft());
- screenshotRect = screenshotRect.intersected(wndRect);
- } else {
- screenshotRect = wndRect;
- }
- } else {
- screenshotRect = screenshotRect.isValid() ? screenshotRect : geometry();
- }
- return const_cast<QAndroidPlatformScreen *>(this)->doScreenShot(screenshotRect.toRect());
-}
-
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h
index edddc9c557..d850d0db09 100644
--- a/src/plugins/platforms/android/qandroidplatformscreen.h
+++ b/src/plugins/platforms/android/qandroidplatformscreen.h
@@ -1,66 +1,30 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMSCREEN_H
#define QANDROIDPLATFORMSCREEN_H
-#include "androidsurfaceclient.h"
-
#include <QList>
#include <QPainter>
#include <QTimer>
#include <QWaitCondition>
#include <QtCore/QJniObject>
#include <qpa/qplatformscreen.h>
-
-#include <android/native_window.h>
+#include <QtGui/qscreen_platform.h>
QT_BEGIN_NAMESPACE
class QAndroidPlatformWindow;
-class QAndroidPlatformScreen: public QObject, public QPlatformScreen, public AndroidSurfaceClient
+
+class QAndroidPlatformScreen : public QObject,
+ public QPlatformScreen,
+ public QNativeInterface::QAndroidScreen
{
Q_OBJECT
public:
- QAndroidPlatformScreen();
+ QAndroidPlatformScreen(const QJniObject &displayObject);
~QAndroidPlatformScreen();
QRect geometry() const override { return QRect(QPoint(), m_size); }
@@ -69,62 +33,51 @@ public:
QImage::Format format() const override { return m_format; }
QSizeF physicalSize() const override { return m_physicalSize; }
- inline QWindow *topWindow() const;
+ QString name() const override { return m_name; }
+ QList<Mode> modes() const override { return m_modes; }
+ int currentMode() const override { return m_currentMode; }
+ int preferredMode() const override { return m_currentMode; }
+ qreal refreshRate() const override { return m_refreshRate; }
+ inline QWindow *topVisibleWindow() const;
QWindow *topLevelAt(const QPoint & p) const override;
- // compositor api
void addWindow(QAndroidPlatformWindow *window);
void removeWindow(QAndroidPlatformWindow *window);
void raise(QAndroidPlatformWindow *window);
void lower(QAndroidPlatformWindow *window);
-
- void scheduleUpdate();
- void topWindowChanged(QWindow *w);
- int rasterSurfaces();
+ void topVisibleWindowChanged();
+ int displayId() const override;
public slots:
- void setDirty(const QRect &rect);
void setPhysicalSize(const QSize &size);
void setAvailableGeometry(const QRect &rect);
void setSize(const QSize &size);
+ void setSizeParameters(const QSize &physicalSize, const QSize &size,
+ const QRect &availableGeometry);
+ void setRefreshRate(qreal refreshRate);
+ void setOrientation(Qt::ScreenOrientation orientation);
protected:
- bool event(QEvent *event) override;
-
typedef QList<QAndroidPlatformWindow *> WindowStackType;
WindowStackType m_windowStack;
- QRect m_dirtyRect;
- bool m_updatePending = false;
-
QRect m_availableGeometry;
int m_depth;
QImage::Format m_format;
QSizeF m_physicalSize;
+ qreal m_refreshRate;
+ QString m_name;
+ QList<Mode> m_modes;
+ int m_currentMode = 0;
+ int m_displayId = -1;
private:
QDpi logicalDpi() const override;
QDpi logicalBaseDpi() const override;
Qt::ScreenOrientation orientation() const override;
Qt::ScreenOrientation nativeOrientation() const override;
- QPixmap grabWindow(WId window, int x, int y, int width, int height) const override;
- void surfaceChanged(JNIEnv *env, jobject surface, int w, int h) override;
- void releaseSurface();
void applicationStateChanged(Qt::ApplicationState);
- QPixmap doScreenShot(QRect grabRect = QRect());
-
-private slots:
- void doRedraw(QImage *screenGrabImage = nullptr);
-
private:
- int m_id = -1;
- QAtomicInt m_rasterSurfaces = 0;
- ANativeWindow* m_nativeSurface = nullptr;
- QWaitCondition m_surfaceWaitCondition;
QSize m_size;
-
- QImage m_lastScreenshot;
- QImage::Format m_pixelFormat = QImage::Format_RGBA8888_Premultiplied;
- bool m_repaintOccurred = false;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformservices.cpp b/src/plugins/platforms/android/qandroidplatformservices.cpp
index 36e1aef609..39287aa905 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.cpp
+++ b/src/plugins/platforms/android/qandroidplatformservices.cpp
@@ -1,80 +1,98 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformservices.h"
#include <QDebug>
+#include <QDesktopServices>
#include <QFile>
#include <QMimeDatabase>
-#include <QUrl>
#include <QtCore/QJniObject>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/qscopedvaluerollback.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QAndroidPlatformServices::QAndroidPlatformServices()
{
+ m_actionView = QJniObject::getStaticObjectField("android/content/Intent", "ACTION_VIEW",
+ "Ljava/lang/String;")
+ .toString();
+
+ QtAndroidPrivate::registerNewIntentListener(this);
+
+ // Qt applications without Activity contexts cannot retrieve intents from the Activity.
+ if (QNativeInterface::QAndroidApplication::isActivityContext()) {
+ QMetaObject::invokeMethod(
+ this,
+ [this] {
+ QJniObject context = QJniObject(QtAndroidPrivate::context());
+ QJniObject intent =
+ context.callObjectMethod("getIntent", "()Landroid/content/Intent;");
+ handleNewIntent(nullptr, intent.object());
+ },
+ Qt::QueuedConnection);
+ }
}
+Q_DECLARE_JNI_CLASS(UriType, "android/net/Uri")
+Q_DECLARE_JNI_CLASS(FileType, "java/io/File")
+Q_DECLARE_JNI_CLASS(File, "java/io/File")
+Q_DECLARE_JNI_CLASS(FileProvider, "androidx/core/content/FileProvider");
+
bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
{
QString mime;
QUrl url(theUrl);
+ // avoid recursing back into self
+ if (url == m_handlingUrl)
+ return false;
+
// if the file is local, we need to pass the MIME type, otherwise Android
// does not start an Intent to view this file
- QLatin1String fileScheme("file");
- if ((url.scheme().isEmpty() || url.scheme() == fileScheme) && QFile::exists(url.path())) {
- // a real URL including the scheme is needed, else the Intent can not be started
+ const auto fileScheme = "file"_L1;
+
+ // a real URL including the scheme is needed, else the Intent can not be started
+ if (url.scheme().isEmpty())
url.setScheme(fileScheme);
- QMimeDatabase mimeDb;
- mime = mimeDb.mimeTypeForUrl(url).name();
- }
+
+ if (url.scheme() == fileScheme)
+ mime = QMimeDatabase().mimeTypeForUrl(url).name();
+
+ const QJniObject mimeString = QJniObject::fromString(mime);
using namespace QNativeInterface;
- QJniObject urlString = QJniObject::fromString(url.toString());
- QJniObject mimeString = QJniObject::fromString(mime);
- return QJniObject::callStaticMethod<jboolean>(
- QtAndroid::applicationClass(), "openURL",
- "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Z",
- QAndroidApplication::context(), urlString.object(), mimeString.object());
+
+ auto openUrl = [mimeString](const QJniObject &url) {
+ return QJniObject::callStaticMethod<jboolean>(QtAndroid::applicationClass(), "openURL",
+ QAndroidApplication::context(), url.object<jstring>(), mimeString.object<jstring>());
+ };
+
+ if (url.scheme() != fileScheme || QNativeInterface::QAndroidApplication::sdkVersion() < 24)
+ return openUrl(QJniObject::fromString(url.toString()));
+
+ // Use FileProvider for file scheme with sdk >= 24
+ const QJniObject context = QAndroidApplication::context();
+ const auto appId = context.callMethod<jstring>("getPackageName").toString();
+ const auto providerName = QJniObject::fromString(appId + ".qtprovider"_L1);
+
+ const auto urlPath = QJniObject::fromString(url.path());
+ const auto urlFile = QJniObject(QtJniTypes::Traits<QtJniTypes::File>::className(),
+ urlPath.object<jstring>());
+
+ const auto fileProviderUri = QJniObject::callStaticMethod<QtJniTypes::UriType>(
+ QtJniTypes::Traits<QtJniTypes::FileProvider>::className(), "getUriForFile",
+ QAndroidApplication::context(), providerName.object<jstring>(),
+ urlFile.object<QtJniTypes::FileType>());
+
+ if (fileProviderUri.isValid())
+ return openUrl(fileProviderUri.callMethod<jstring>("toString"));
+
+ return false;
}
bool QAndroidPlatformServices::openDocument(const QUrl &url)
@@ -87,4 +105,19 @@ QByteArray QAndroidPlatformServices::desktopEnvironment() const
return QByteArray("Android");
}
+bool QAndroidPlatformServices::handleNewIntent(JNIEnv *env, jobject intent)
+{
+ Q_UNUSED(env);
+
+ const QJniObject jniIntent(intent);
+
+ const QString action = jniIntent.callObjectMethod<jstring>("getAction").toString();
+ if (action != m_actionView)
+ return false;
+
+ const QString url = jniIntent.callObjectMethod<jstring>("getDataString").toString();
+ QScopedValueRollback<QUrl> rollback(m_handlingUrl, url);
+ return QDesktopServices::openUrl(url);
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformservices.h b/src/plugins/platforms/android/qandroidplatformservices.h
index 6f2f0a394f..762d14deeb 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.h
+++ b/src/plugins/platforms/android/qandroidplatformservices.h
@@ -1,57 +1,33 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDPLATFORMDESKTOPSERVICE_H
#define ANDROIDPLATFORMDESKTOPSERVICE_H
#include <qpa/qplatformservices.h>
#include "androidjnimain.h"
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qobject.h>
+#include <QUrl>
QT_BEGIN_NAMESPACE
-class QAndroidPlatformServices: public QPlatformServices
+class QAndroidPlatformServices : public QObject,
+ public QPlatformServices,
+ public QtAndroidPrivate::NewIntentListener
{
public:
QAndroidPlatformServices();
+
bool openUrl(const QUrl &url) override;
bool openDocument(const QUrl &url) override;
QByteArray desktopEnvironment() const override;
+
+ bool handleNewIntent(JNIEnv *env, jobject intent) override;
+
+private:
+ QUrl m_handlingUrl;
+ QString m_actionView;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp
index 30a967cc26..7b9072df69 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.cpp
+++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp
@@ -1,45 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidjnimain.h"
#include "androidjnimenu.h"
#include "qandroidplatformtheme.h"
+#include "qandroidplatformiconengine.h"
#include "qandroidplatformmenubar.h"
#include "qandroidplatformmenu.h"
#include "qandroidplatformmenuitem.h"
@@ -58,6 +23,10 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaMenus, "qt.qpa.menus")
+
+using namespace Qt::StringLiterals;
+
namespace {
const int textStyle_bold = 1;
const int textStyle_italic = 2;
@@ -69,44 +38,44 @@ namespace {
static int fontType(const QString &androidControl)
{
- if (androidControl == QLatin1String("defaultStyle"))
+ if (androidControl == "defaultStyle"_L1)
return QPlatformTheme::SystemFont;
- if (androidControl == QLatin1String("textViewStyle"))
+ if (androidControl == "textViewStyle"_L1)
return QPlatformTheme::LabelFont;
- else if (androidControl == QLatin1String("buttonStyle"))
+ else if (androidControl == "buttonStyle"_L1)
return QPlatformTheme::PushButtonFont;
- else if (androidControl == QLatin1String("checkboxStyle"))
+ else if (androidControl == "checkboxStyle"_L1)
return QPlatformTheme::CheckBoxFont;
- else if (androidControl == QLatin1String("radioButtonStyle"))
+ else if (androidControl == "radioButtonStyle"_L1)
return QPlatformTheme::RadioButtonFont;
- else if (androidControl == QLatin1String("simple_list_item_single_choice"))
+ else if (androidControl == "simple_list_item_single_choice"_L1)
return QPlatformTheme::ItemViewFont;
- else if (androidControl == QLatin1String("simple_spinner_dropdown_item"))
+ else if (androidControl == "simple_spinner_dropdown_item"_L1)
return QPlatformTheme::ComboMenuItemFont;
- else if (androidControl == QLatin1String("spinnerStyle"))
+ else if (androidControl == "spinnerStyle"_L1)
return QPlatformTheme::ComboLineEditFont;
- else if (androidControl == QLatin1String("simple_list_item"))
+ else if (androidControl == "simple_list_item"_L1)
return QPlatformTheme::ListViewFont;
return -1;
}
static int paletteType(const QString &androidControl)
{
- if (androidControl == QLatin1String("defaultStyle"))
+ if (androidControl == "defaultStyle"_L1)
return QPlatformTheme::SystemPalette;
- if (androidControl == QLatin1String("textViewStyle"))
+ if (androidControl == "textViewStyle"_L1)
return QPlatformTheme::LabelPalette;
- else if (androidControl == QLatin1String("buttonStyle"))
+ else if (androidControl == "buttonStyle"_L1)
return QPlatformTheme::ButtonPalette;
- else if (androidControl == QLatin1String("checkboxStyle"))
+ else if (androidControl == "checkboxStyle"_L1)
return QPlatformTheme::CheckBoxPalette;
- else if (androidControl == QLatin1String("radioButtonStyle"))
+ else if (androidControl == "radioButtonStyle"_L1)
return QPlatformTheme::RadioButtonPalette;
- else if (androidControl == QLatin1String("simple_list_item_single_choice"))
+ else if (androidControl == "simple_list_item_single_choice"_L1)
return QPlatformTheme::ItemViewPalette;
- else if (androidControl == QLatin1String("editTextStyle"))
+ else if (androidControl == "editTextStyle"_L1)
return QPlatformTheme::TextLineEditPalette;
- else if (androidControl == QLatin1String("spinnerStyle"))
+ else if (androidControl == "spinnerStyle"_L1)
return QPlatformTheme::ComboBoxPalette;
return -1;
}
@@ -118,17 +87,17 @@ static void setPaletteColor(const QVariantMap &object,
// QPalette::Active -> ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET
palette.setColor(QPalette::Active,
role,
- QRgb(object.value(QLatin1String("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET")).toInt()));
+ QRgb(object.value("ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET"_L1).toInt()));
// QPalette::Inactive -> ENABLED_STATE_SET
palette.setColor(QPalette::Inactive,
role,
- QRgb(object.value(QLatin1String("ENABLED_STATE_SET")).toInt()));
+ QRgb(object.value("ENABLED_STATE_SET"_L1).toInt()));
// QPalette::Disabled -> EMPTY_STATE_SET
palette.setColor(QPalette::Disabled,
role,
- QRgb(object.value(QLatin1String("EMPTY_STATE_SET")).toInt()));
+ QRgb(object.value("EMPTY_STATE_SET"_L1).toInt()));
palette.setColor(QPalette::Current, role, palette.color(QPalette::Active, role));
@@ -137,17 +106,17 @@ static void setPaletteColor(const QVariantMap &object,
// QPalette::Active -> PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET
palette.setColor(QPalette::Active,
QPalette::BrightText,
- QRgb(object.value(QLatin1String("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET")).toInt()));
+ QRgb(object.value("PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET"_L1).toInt()));
// QPalette::Inactive -> PRESSED_ENABLED_STATE_SET
palette.setColor(QPalette::Inactive,
QPalette::BrightText,
- QRgb(object.value(QLatin1String("PRESSED_ENABLED_STATE_SET")).toInt()));
+ QRgb(object.value("PRESSED_ENABLED_STATE_SET"_L1).toInt()));
// QPalette::Disabled -> PRESSED_STATE_SET
palette.setColor(QPalette::Disabled,
QPalette::BrightText,
- QRgb(object.value(QLatin1String("PRESSED_STATE_SET")).toInt()));
+ QRgb(object.value("PRESSED_STATE_SET"_L1).toInt()));
palette.setColor(QPalette::Current, QPalette::BrightText, palette.color(QPalette::Active, QPalette::BrightText));
@@ -155,17 +124,17 @@ static void setPaletteColor(const QVariantMap &object,
// QPalette::Active -> ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET
palette.setColor(QPalette::Active,
QPalette::HighlightedText,
- QRgb(object.value(QLatin1String("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET")).toInt()));
+ QRgb(object.value("ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET"_L1).toInt()));
// QPalette::Inactive -> ENABLED_SELECTED_STATE_SET
palette.setColor(QPalette::Inactive,
QPalette::HighlightedText,
- QRgb(object.value(QLatin1String("ENABLED_SELECTED_STATE_SET")).toInt()));
+ QRgb(object.value("ENABLED_SELECTED_STATE_SET"_L1).toInt()));
// QPalette::Disabled -> SELECTED_STATE_SET
palette.setColor(QPalette::Disabled,
QPalette::HighlightedText,
- QRgb(object.value(QLatin1String("SELECTED_STATE_SET")).toInt()));
+ QRgb(object.value("SELECTED_STATE_SET"_L1).toInt()));
palette.setColor(QPalette::Current,
QPalette::HighlightedText,
@@ -187,25 +156,17 @@ static void setPaletteColor(const QVariantMap &object,
QJsonObject AndroidStyle::loadStyleData()
{
- QString stylePath(QLatin1String(qgetenv("MINISTRO_ANDROID_STYLE_PATH")));
+ QString stylePath(QLatin1StringView(qgetenv("ANDROID_STYLE_PATH")));
const QLatin1Char slashChar('/');
if (!stylePath.isEmpty() && !stylePath.endsWith(slashChar))
stylePath += slashChar;
- QString androidTheme = QLatin1String(qgetenv("QT_ANDROID_THEME"));
- if (!androidTheme.isEmpty() && !androidTheme.endsWith(slashChar))
- androidTheme += slashChar;
+ if (QAndroidPlatformIntegration::colorScheme() == Qt::ColorScheme::Dark)
+ stylePath += "darkUiMode/"_L1;
- if (stylePath.isEmpty()) {
- stylePath = QLatin1String("/data/data/org.kde.necessitas.ministro/files/dl/style/")
- + QLatin1String(qgetenv("QT_ANDROID_THEME_DISPLAY_DPI")) + slashChar;
- }
Q_ASSERT(!stylePath.isEmpty());
- if (!androidTheme.isEmpty() && QFileInfo::exists(stylePath + androidTheme + QLatin1String("style.json")))
- stylePath += androidTheme;
-
- QFile f(stylePath + QLatin1String("style.json"));
+ QFile f(stylePath + "style.json"_L1);
if (!f.open(QIODevice::ReadOnly))
return QJsonObject();
@@ -223,13 +184,27 @@ QJsonObject AndroidStyle::loadStyleData()
return document.object();
}
-static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette)
+static void loadAndroidStyle(QPalette *defaultPalette, std::shared_ptr<AndroidStyle> &style)
{
double pixelDensity = QHighDpiScaling::isActive() ? QtAndroid::pixelDensity() : 1.0;
- std::shared_ptr<AndroidStyle> style = std::make_shared<AndroidStyle>();
+ if (style) {
+ style->m_standardPalette = QPalette();
+ style->m_palettes.clear();
+ style->m_fonts.clear();
+ style->m_QWidgetsFonts.clear();
+ } else {
+ style = std::make_shared<AndroidStyle>();
+ }
+
style->m_styleData = AndroidStyle::loadStyleData();
+
if (style->m_styleData.isEmpty())
- return std::shared_ptr<AndroidStyle>();
+ return;
+
+ {
+ QFont font("Droid Sans Mono"_L1, 14.0 * 100 / 72);
+ style->m_fonts.insert(QPlatformTheme::FixedFont, font);
+ }
for (QJsonObject::const_iterator objectIterator = style->m_styleData.constBegin();
objectIterator != style->m_styleData.constEnd();
@@ -241,7 +216,7 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette)
continue;
}
QJsonObject item = value.toObject();
- QJsonObject::const_iterator attributeIterator = item.find(QLatin1String("qtClass"));
+ QJsonObject::const_iterator attributeIterator = item.find("qtClass"_L1);
QByteArray qtClassName;
if (attributeIterator != item.constEnd()) {
// The item has palette and font information for a specific Qt Class (e.g. QWidget, QPushButton, etc.)
@@ -253,12 +228,12 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette)
QFont font;
// Font size (in pixels)
- attributeIterator = item.find(QLatin1String("TextAppearance_textSize"));
+ attributeIterator = item.find("TextAppearance_textSize"_L1);
if (attributeIterator != item.constEnd())
font.setPixelSize(int(attributeIterator.value().toDouble() / pixelDensity));
// Font style
- attributeIterator = item.find(QLatin1String("TextAppearance_textStyle"));
+ attributeIterator = item.find("TextAppearance_textStyle"_L1);
if (attributeIterator != item.constEnd()) {
const int style = int(attributeIterator.value().toDouble());
font.setBold(style & textStyle_bold);
@@ -266,7 +241,7 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette)
}
// Font typeface
- attributeIterator = item.find(QLatin1String("TextAppearance_typeface"));
+ attributeIterator = item.find("TextAppearance_typeface"_L1);
if (attributeIterator != item.constEnd()) {
QFont::StyleHint styleHint = QFont::AnyStyle;
switch (int(attributeIterator.value().toDouble())) {
@@ -298,23 +273,23 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette)
// Extract palette information
QPalette palette = *defaultPalette;
- attributeIterator = item.find(QLatin1String("defaultTextColorPrimary"));
+ attributeIterator = item.find("defaultTextColorPrimary"_L1);
if (attributeIterator != item.constEnd())
palette.setColor(QPalette::WindowText, QRgb(int(attributeIterator.value().toDouble())));
- attributeIterator = item.find(QLatin1String("defaultBackgroundColor"));
+ attributeIterator = item.find("defaultBackgroundColor"_L1);
if (attributeIterator != item.constEnd())
palette.setColor(QPalette::Window, QRgb(int(attributeIterator.value().toDouble())));
- attributeIterator = item.find(QLatin1String("TextAppearance_textColor"));
+ attributeIterator = item.find("TextAppearance_textColor"_L1);
if (attributeIterator != item.constEnd())
setPaletteColor(attributeIterator.value().toObject().toVariantMap(), palette, QPalette::WindowText);
- attributeIterator = item.find(QLatin1String("TextAppearance_textColorLink"));
+ attributeIterator = item.find("TextAppearance_textColorLink"_L1);
if (attributeIterator != item.constEnd())
setPaletteColor(attributeIterator.value().toObject().toVariantMap(), palette, QPalette::Link);
- attributeIterator = item.find(QLatin1String("TextAppearance_textColorHighlight"));
+ attributeIterator = item.find("TextAppearance_textColorHighlight"_L1);
if (attributeIterator != item.constEnd())
palette.setColor(QPalette::Highlight, QRgb(int(attributeIterator.value().toDouble())));
@@ -326,11 +301,43 @@ static std::shared_ptr<AndroidStyle> loadAndroidStyle(QPalette *defaultPalette)
// Extract palette information
}
}
- return style;
+}
+
+QAndroidPlatformTheme *QAndroidPlatformTheme::m_instance = nullptr;
+
+QAndroidPlatformTheme *QAndroidPlatformTheme::instance(
+ QAndroidPlatformNativeInterface *androidPlatformNativeInterface)
+{
+ if (androidPlatformNativeInterface && !m_instance) {
+ m_instance = new QAndroidPlatformTheme(androidPlatformNativeInterface);
+ }
+ return m_instance;
}
QAndroidPlatformTheme::QAndroidPlatformTheme(QAndroidPlatformNativeInterface *androidPlatformNativeInterface)
{
+ updateStyle();
+
+ androidPlatformNativeInterface->m_androidStyle = m_androidStyleData;
+
+ // default in case the style has not set a font
+ m_systemFont = QFont("Roboto"_L1, 14.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi
+}
+
+QAndroidPlatformTheme::~QAndroidPlatformTheme()
+{
+ m_instance = nullptr;
+}
+
+void QAndroidPlatformTheme::updateColorScheme()
+{
+ updateStyle();
+ QWindowSystemInterface::handleThemeChange();
+}
+
+void QAndroidPlatformTheme::updateStyle()
+{
+ QColor windowText = Qt::black;
QColor background(229, 229, 229);
QColor light = background.lighter(150);
QColor mid(background.darker(130));
@@ -347,7 +354,27 @@ QAndroidPlatformTheme::QAndroidPlatformTheme(QAndroidPlatformNativeInterface *an
QColor highlight(148, 210, 231);
QColor disabledShadow = shadow.lighter(150);
- m_defaultPalette = QPalette(Qt::black,background,light,dark,mid,text,base);
+ if (colorScheme() == Qt::ColorScheme::Dark) {
+ // Colors were prepared based on Theme.DeviceDefault.DayNight
+ windowText = QColor(250, 250, 250);
+ background = QColor(48, 48, 48);
+ light = background.darker(150);
+ mid = background.lighter(130);
+ midLight = mid.darker(110);
+ base = background;
+ disabledBase = background;
+ dark = background.darker(150);
+ darkDisabled = dark.darker(110);
+ text = QColor(250, 250, 250);
+ highlightedText = QColor(250, 250, 250);
+ disabledText = QColor(96, 96, 96);
+ button = QColor(48, 48, 48);
+ shadow = QColor(32, 32, 32);
+ highlight = QColor(102, 178, 204);
+ disabledShadow = shadow.darker(150);
+ }
+
+ m_defaultPalette = QPalette(windowText,background,light,dark,mid,text,base);
m_defaultPalette.setBrush(QPalette::Midlight, midLight);
m_defaultPalette.setBrush(QPalette::Button, button);
m_defaultPalette.setBrush(QPalette::Shadow, shadow);
@@ -363,34 +390,42 @@ QAndroidPlatformTheme::QAndroidPlatformTheme(QAndroidPlatformNativeInterface *an
m_defaultPalette.setBrush(QPalette::Active, QPalette::Highlight, highlight);
m_defaultPalette.setBrush(QPalette::Inactive, QPalette::Highlight, highlight);
m_defaultPalette.setBrush(QPalette::Disabled, QPalette::Highlight, highlight.lighter(150));
- m_androidStyleData = loadAndroidStyle(&m_defaultPalette);
- QGuiApplication::setPalette(m_defaultPalette);
- androidPlatformNativeInterface->m_androidStyle = m_androidStyleData;
- // default in case the style has not set a font
- m_systemFont = QFont(QLatin1String("Roboto"), 14.0 * 100 / 72); // keep default size the same after changing from 100 dpi to 72 dpi
+ loadAndroidStyle(&m_defaultPalette, m_androidStyleData);
}
QPlatformMenuBar *QAndroidPlatformTheme::createPlatformMenuBar() const
{
- return new QAndroidPlatformMenuBar;
+ auto *menuBar = new QAndroidPlatformMenuBar;
+ qCDebug(lcQpaMenus) << "Created" << menuBar;
+ return menuBar;
}
QPlatformMenu *QAndroidPlatformTheme::createPlatformMenu() const
{
- return new QAndroidPlatformMenu;
+ auto *menu = new QAndroidPlatformMenu;
+ qCDebug(lcQpaMenus) << "Created" << menu;
+ return menu;
}
QPlatformMenuItem *QAndroidPlatformTheme::createPlatformMenuItem() const
{
- return new QAndroidPlatformMenuItem;
+ auto *menuItem = new QAndroidPlatformMenuItem;
+ qCDebug(lcQpaMenus) << "Created" << menuItem;
+ return menuItem;
}
void QAndroidPlatformTheme::showPlatformMenuBar()
{
+ qCDebug(lcQpaMenus) << "Showing platform menu bar";
QtAndroidMenu::openOptionsMenu();
}
+Qt::ColorScheme QAndroidPlatformTheme::colorScheme() const
+{
+ return QAndroidPlatformIntegration::colorScheme();
+}
+
static inline int paletteType(QPlatformTheme::Palette type)
{
switch (type) {
@@ -456,15 +491,20 @@ const QFont *QAndroidPlatformTheme::font(Font type) const
return 0;
}
+QIconEngine *QAndroidPlatformTheme::createIconEngine(const QString &iconName) const
+{
+ return new QAndroidPlatformIconEngine(iconName);
+}
+
QVariant QAndroidPlatformTheme::themeHint(ThemeHint hint) const
{
switch (hint) {
case StyleNames:
if (qEnvironmentVariableIntValue("QT_USE_ANDROID_NATIVE_STYLE")
&& m_androidStyleData) {
- return QStringList(QLatin1String("android"));
+ return QStringList("android"_L1);
}
- return QStringList(QLatin1String("Fusion"));
+ return QStringList("Fusion"_L1);
case DialogButtonBoxLayout:
return QVariant(QPlatformDialogHelper::AndroidLayout);
case MouseDoubleClickDistance:
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.h b/src/plugins/platforms/android/qandroidplatformtheme.h
index b49d2516f1..ce3d6d5f73 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.h
+++ b/src/plugins/platforms/android/qandroidplatformtheme.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMTHEME_H
#define QANDROIDPLATFORMTHEME_H
@@ -43,13 +7,17 @@
#include <qpa/qplatformtheme.h>
#include <QtGui/qfont.h>
#include <QtGui/qpalette.h>
-
+#include <QtCore/qhash.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qloggingcategory.h>
#include <QJsonObject>
#include <memory>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaMenus)
+
struct AndroidStyle
{
static QJsonObject loadStyleData();
@@ -64,20 +32,28 @@ class QAndroidPlatformNativeInterface;
class QAndroidPlatformTheme: public QPlatformTheme
{
public:
- QAndroidPlatformTheme(QAndroidPlatformNativeInterface * androidPlatformNativeInterface);
+ ~QAndroidPlatformTheme();
+ void updateColorScheme();
+ void updateStyle();
QPlatformMenuBar *createPlatformMenuBar() const override;
QPlatformMenu *createPlatformMenu() const override;
QPlatformMenuItem *createPlatformMenuItem() const override;
void showPlatformMenuBar() override;
+ Qt::ColorScheme colorScheme() const override;
const QPalette *palette(Palette type = SystemPalette) const override;
const QFont *font(Font type = SystemFont) const override;
+ QIconEngine *createIconEngine(const QString &iconName) const override;
QVariant themeHint(ThemeHint hint) const override;
QString standardButtonText(int button) const override;
bool usePlatformNativeDialog(DialogType type) const override;
QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override;
+ static QAndroidPlatformTheme *instance(
+ QAndroidPlatformNativeInterface * androidPlatformNativeInterface = nullptr);
private:
+ QAndroidPlatformTheme(QAndroidPlatformNativeInterface * androidPlatformNativeInterface);
+ static QAndroidPlatformTheme * m_instance;
std::shared_ptr<AndroidStyle> m_androidStyleData;
QPalette m_defaultPalette;
QFont m_systemFont;
diff --git a/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp b/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp
index a411d0f007..8d90b9dea1 100644
--- a/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp
+++ b/src/plugins/platforms/android/qandroidplatformvulkaninstance.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformvulkaninstance.h"
diff --git a/src/plugins/platforms/android/qandroidplatformvulkaninstance.h b/src/plugins/platforms/android/qandroidplatformvulkaninstance.h
index 80f73215ad..ad7de71867 100644
--- a/src/plugins/platforms/android/qandroidplatformvulkaninstance.h
+++ b/src/plugins/platforms/android/qandroidplatformvulkaninstance.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMVULKANINSTANCE_H
#define QANDROIDPLATFORMVULKANINSTANCE_H
diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
index 9d4688b52d..4bf4f44fa1 100644
--- a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
+++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androiddeadlockprotector.h"
#include "androidjnimain.h"
@@ -54,7 +18,6 @@ QT_BEGIN_NAMESPACE
QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
: QAndroidPlatformWindow(window),
- m_nativeSurfaceId(-1),
m_nativeWindow(nullptr),
m_vkSurface(0),
m_createVkSurface(nullptr),
@@ -65,11 +28,7 @@ QAndroidPlatformVulkanWindow::QAndroidPlatformVulkanWindow(QWindow *window)
QAndroidPlatformVulkanWindow::~QAndroidPlatformVulkanWindow()
{
m_surfaceWaitCondition.wakeOne();
- lockSurface();
- if (m_nativeSurfaceId != -1)
- QtAndroid::destroySurface(m_nativeSurfaceId);
- clearSurface();
- unlockSurface();
+ destroyAndClearSurface();
}
void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
@@ -80,8 +39,8 @@ void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
m_oldGeometry = geometry();
QAndroidPlatformWindow::setGeometry(rect);
- if (m_nativeSurfaceId != -1)
- QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect);
+ if (m_surfaceCreated)
+ setNativeGeometry(rect);
QRect availableGeometry = screen()->availableGeometry();
if (rect.width() > 0
@@ -90,22 +49,13 @@ void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect)
&& availableGeometry.height() > 0) {
QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size()));
}
-
- if (rect.topLeft() != m_oldGeometry.topLeft())
- repaint(QRegion(rect));
}
void QAndroidPlatformVulkanWindow::applicationStateChanged(Qt::ApplicationState state)
{
QAndroidPlatformWindow::applicationStateChanged(state);
if (state <= Qt::ApplicationHidden) {
- lockSurface();
- if (m_nativeSurfaceId != -1) {
- QtAndroid::destroySurface(m_nativeSurfaceId);
- m_nativeSurfaceId = -1;
- }
- clearSurface();
- unlockSurface();
+ destroyAndClearSurface();
}
}
@@ -127,27 +77,12 @@ void QAndroidPlatformVulkanWindow::clearSurface()
}
}
-void QAndroidPlatformVulkanWindow::sendExpose()
-{
- QRect availableGeometry = screen()->availableGeometry();
- if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
- QWindowSystemInterface::handleExposeEvent(window(), QRegion(QRect(QPoint(), geometry().size())));
-}
-
-void QAndroidPlatformVulkanWindow::surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h)
+void QAndroidPlatformVulkanWindow::destroyAndClearSurface()
{
- Q_UNUSED(jniEnv);
- Q_UNUSED(w);
- Q_UNUSED(h);
-
lockSurface();
- m_androidSurfaceObject = surface;
- if (surface)
- m_surfaceWaitCondition.wakeOne();
+ destroySurface();
+ clearSurface();
unlockSurface();
-
- if (surface)
- sendExpose();
}
VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
@@ -160,16 +95,15 @@ VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
clearSurface();
QMutexLocker lock(&m_surfaceMutex);
- if (m_nativeSurfaceId == -1) {
+ if (!m_surfaceCreated) {
AndroidDeadlockProtector protector;
if (!protector.acquire())
return &m_vkSurface;
- const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
- m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
+ createSurface();
m_surfaceWaitCondition.wait(&m_surfaceMutex);
}
- if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid())
+ if (!m_surfaceCreated || !m_androidSurfaceObject.isValid())
return &m_vkSurface;
QJniEnvironment env;
diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.h b/src/plugins/platforms/android/qandroidplatformvulkanwindow.h
index bc11f468d6..fa959239d1 100644
--- a/src/plugins/platforms/android/qandroidplatformvulkanwindow.h
+++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDPLATFORMVULKANWINDOW_H
#define QANDROIDPLATFORMVULKANWINDOW_H
@@ -46,7 +10,6 @@
#define VK_USE_PLATFORM_ANDROID_KHR
-#include "androidsurfaceclient.h"
#include "qandroidplatformvulkaninstance.h"
#include "qandroidplatformwindow.h"
@@ -56,7 +19,7 @@
QT_BEGIN_NAMESPACE
-class QAndroidPlatformVulkanWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient
+class QAndroidPlatformVulkanWindow : public QAndroidPlatformWindow
{
public:
explicit QAndroidPlatformVulkanWindow(QWindow *window);
@@ -68,17 +31,11 @@ public:
VkSurfaceKHR *vkSurface();
-protected:
- void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h) override;
-
private:
- void sendExpose();
void clearSurface();
+ void destroyAndClearSurface();
- int m_nativeSurfaceId;
ANativeWindow *m_nativeWindow;
- QJniObject m_androidSurfaceObject;
- QWaitCondition m_surfaceWaitCondition;
QSurfaceFormat m_format;
QRect m_oldGeometry;
VkSurfaceKHR m_vkSurface;
diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp
index e1cf2487fe..979f0fb98a 100644
--- a/src/plugins/platforms/android/qandroidplatformwindow.cpp
+++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qandroidplatformwindow.h"
#include "qandroidplatformopenglcontext.h"
@@ -50,40 +14,95 @@
QT_BEGIN_NAMESPACE
-static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
- : QPlatformWindow(window)
+ : QPlatformWindow(window), m_nativeQtWindow(nullptr),
+ m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(nullptr),
+ m_androidSurfaceObject(nullptr)
{
m_windowFlags = Qt::Widget;
m_windowState = Qt::WindowNoState;
- m_windowId = winIdGenerator.fetchAndAddRelaxed(1) + 1;
+ // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
+ // the fact that it's a raster window for now
+ m_isRaster = window->surfaceType() == QSurface::RasterSurface;
setWindowState(window->windowStates());
// the following is in relation to the virtual geometry
const bool forceMaximize = m_windowState & (Qt::WindowMaximized | Qt::WindowFullScreen);
- const QRect requestedNativeGeometry =
- forceMaximize ? QRect() : QHighDpi::toNativePixels(window->geometry(), window);
- const QRect availableDeviceIndependentGeometry = (window->parent())
- ? window->parent()->geometry()
- : QHighDpi::fromNativePixels(platformScreen()->availableGeometry(), window);
+ const QRect nativeScreenGeometry = platformScreen()->availableGeometry();
+ if (forceMaximize) {
+ setGeometry(nativeScreenGeometry);
+ } else {
+ const QRect requestedNativeGeometry = QHighDpi::toNativePixels(window->geometry(), window);
+ const QRect availableDeviceIndependentGeometry = (window->parent())
+ ? window->parent()->geometry()
+ : QHighDpi::fromNativePixels(nativeScreenGeometry, window);
+ // initialGeometry returns in native pixels
+ const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
+ window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
+ availableDeviceIndependentGeometry.height());
+ if (requestedNativeGeometry != finalNativeGeometry)
+ setGeometry(finalNativeGeometry);
+ }
+
+ if (isEmbeddingContainer())
+ return;
+
+ if (parent()) {
+ QAndroidPlatformWindow *androidParent = static_cast<QAndroidPlatformWindow*>(parent());
+ if (!androidParent->isEmbeddingContainer())
+ m_nativeParentQtWindow = androidParent->nativeWindow();
+ }
+
+ m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
+ QNativeInterface::QAndroidApplication::context(),
+ m_nativeParentQtWindow,
+ QtAndroid::qtInputDelegate());
+ m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
- // initialGeometry returns in native pixels
- const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
- window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
- availableDeviceIndependentGeometry.height());
+ if (window->isTopLevel())
+ platformScreen()->addWindow(this);
- if (requestedNativeGeometry != finalNativeGeometry)
- setGeometry(finalNativeGeometry);
+ static bool ok = false;
+ static const int value = qEnvironmentVariableIntValue("QT_ANDROID_SURFACE_CONTAINER_TYPE", &ok);
+ if (ok) {
+ static const SurfaceContainer type = static_cast<SurfaceContainer>(value);
+ if (type == SurfaceContainer::SurfaceView || type == SurfaceContainer::TextureView)
+ m_surfaceContainerType = type;
+ } else if (platformScreen()->windows().size() <= 1) {
+ // TODO should handle case where this changes at runtime -> need to change existing window
+ // into TextureView (or perhaps not, if the parent window would be SurfaceView, as long as
+ // onTop was false it would stay below the children)
+ m_surfaceContainerType = SurfaceContainer::SurfaceView;
+ }
+ qCDebug(lcQpaWindow) << "Window" << m_nativeViewId << "using surface container type"
+ << static_cast<int>(m_surfaceContainerType);
}
+QAndroidPlatformWindow::~QAndroidPlatformWindow()
+{
+ if (window()->isTopLevel())
+ platformScreen()->removeWindow(this);
+}
+
+
void QAndroidPlatformWindow::lower()
{
+ if (m_nativeParentQtWindow.isValid()) {
+ m_nativeParentQtWindow.callMethod<void>("bringChildToBack", nativeViewId());
+ return;
+ }
platformScreen()->lower(this);
}
void QAndroidPlatformWindow::raise()
{
+ if (m_nativeParentQtWindow.isValid()) {
+ m_nativeParentQtWindow.callMethod<void>("bringChildToFront", nativeViewId());
+ QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
+ return;
+ }
updateSystemUiVisibility();
platformScreen()->raise(this);
}
@@ -107,23 +126,25 @@ void QAndroidPlatformWindow::setGeometry(const QRect &rect)
void QAndroidPlatformWindow::setVisible(bool visible)
{
- if (visible)
- updateSystemUiVisibility();
+ if (isEmbeddingContainer())
+ return;
+ m_nativeQtWindow.callMethod<void>("setVisible", visible);
if (visible) {
- if ((m_windowState & Qt::WindowFullScreen)
- || ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) {
- setGeometry(platformScreen()->geometry());
- } else if (m_windowState & Qt::WindowMaximized) {
- setGeometry(platformScreen()->availableGeometry());
+ if (window()->isTopLevel()) {
+ updateSystemUiVisibility();
+ if ((m_windowState & Qt::WindowFullScreen)
+ || ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) {
+ setGeometry(platformScreen()->geometry());
+ } else if (m_windowState & Qt::WindowMaximized) {
+ setGeometry(platformScreen()->availableGeometry());
+ }
+ requestActivateWindow();
}
+ } else if (window()->isTopLevel() && window() == qGuiApp->focusWindow()) {
+ platformScreen()->topVisibleWindowChanged();
}
- if (visible)
- platformScreen()->addWindow(this);
- else
- platformScreen()->removeWindow(this);
-
QRect availableGeometry = screen()->availableGeometry();
if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
QPlatformWindow::setVisible(visible);
@@ -156,7 +177,30 @@ Qt::WindowFlags QAndroidPlatformWindow::windowFlags() const
void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
{
- Q_UNUSED(window);
+ using namespace QtJniTypes;
+
+ if (window) {
+ auto androidWindow = static_cast<const QAndroidPlatformWindow*>(window);
+ if (androidWindow->isEmbeddingContainer())
+ return;
+ // If we were a top level window, remove from screen
+ if (!m_nativeParentQtWindow.isValid())
+ platformScreen()->removeWindow(this);
+
+ const QtWindow parentWindow = androidWindow->nativeWindow();
+ // If this was a child window of another window, the java method takes care of that
+ m_nativeQtWindow.callMethod<void, QtWindow>("setParent", parentWindow.object());
+ m_nativeParentQtWindow = parentWindow;
+ } else if (QtAndroid::isQtApplication()) {
+ m_nativeQtWindow.callMethod<void, QtWindow>("setParent", nullptr);
+ m_nativeParentQtWindow = QJniObject();
+ platformScreen()->addWindow(this);
+ }
+}
+
+WId QAndroidPlatformWindow::winId() const
+{
+ return m_nativeQtWindow.isValid() ? reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
}
QAndroidPlatformScreen *QAndroidPlatformWindow::platformScreen() const
@@ -171,7 +215,9 @@ void QAndroidPlatformWindow::propagateSizeHints()
void QAndroidPlatformWindow::requestActivateWindow()
{
- platformScreen()->topWindowChanged(window());
+ // raise() will handle differences between top level and child windows, and requesting focus
+ if (!blockedByModal())
+ raise();
}
void QAndroidPlatformWindow::updateSystemUiVisibility()
@@ -205,4 +251,134 @@ void QAndroidPlatformWindow::applicationStateChanged(Qt::ApplicationState)
QWindowSystemInterface::flushWindowSystemEvents();
}
+void QAndroidPlatformWindow::createSurface()
+{
+ const QRect rect = geometry();
+ jint x = 0, y = 0, w = -1, h = -1;
+ if (!rect.isNull()) {
+ x = rect.x();
+ y = rect.y();
+ w = std::max(rect.width(), 1);
+ h = std::max(rect.height(), 1);
+ }
+
+ const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
+ const bool isOpaque = !format().hasAlpha() && qFuzzyCompare(window()->opacity(), 1.0);
+
+ m_nativeQtWindow.callMethod<void>("createSurface", windowStaysOnTop, x, y, w, h, 32, isOpaque,
+ m_surfaceContainerType);
+ m_surfaceCreated = true;
+}
+
+void QAndroidPlatformWindow::destroySurface()
+{
+ if (m_surfaceCreated) {
+ m_nativeQtWindow.callMethod<void>("destroySurface");
+ m_surfaceCreated = false;
+ }
+}
+
+void QAndroidPlatformWindow::setNativeGeometry(const QRect &geometry)
+{
+ if (!m_surfaceCreated)
+ return;
+
+ jint x = 0;
+ jint y = 0;
+ jint w = -1;
+ jint h = -1;
+ if (!geometry.isNull()) {
+ x = geometry.x();
+ y = geometry.y();
+ w = geometry.width();
+ h = geometry.height();
+ }
+ m_nativeQtWindow.callMethod<void>("setGeometry", x, y, w, h);
+}
+
+void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
+{
+ lockSurface();
+ m_androidSurfaceObject = surface;
+ if (m_androidSurfaceObject.isValid()) // wait until we have a valid surface to draw into
+ m_surfaceWaitCondition.wakeOne();
+ unlockSurface();
+
+ if (m_androidSurfaceObject.isValid()) {
+ // repaint the window, when we have a valid surface
+ sendExpose();
+ }
+}
+
+void QAndroidPlatformWindow::sendExpose() const
+{
+ QRect availableGeometry = screen()->availableGeometry();
+ if (!geometry().isNull() && !availableGeometry.isNull()) {
+ QWindowSystemInterface::handleExposeEvent(window(),
+ QRegion(QRect(QPoint(), geometry().size())));
+ }
+}
+
+bool QAndroidPlatformWindow::blockedByModal() const
+{
+ QWindow *modalWindow = QGuiApplication::modalWindow();
+ return modalWindow && modalWindow != window();
+}
+
+bool QAndroidPlatformWindow::isEmbeddingContainer() const
+{
+ // Returns true if the window is a wrapper for a foreign window solely to allow embedding Qt
+ // into a native Android app, in which case we should not try to control it more than we "need" to
+ return !QtAndroid::isQtApplication() && window()->isTopLevel();
+}
+
+void QAndroidPlatformWindow::setSurface(JNIEnv *env, jobject object, jint windowId,
+ QtJniTypes::Surface surface)
+{
+ Q_UNUSED(env)
+ Q_UNUSED(object)
+
+ if (!qGuiApp)
+ return;
+
+ const QList<QWindow*> windows = qGuiApp->allWindows();
+ for (QWindow * window : windows) {
+ if (!window->handle())
+ continue;
+ QAndroidPlatformWindow *platformWindow =
+ static_cast<QAndroidPlatformWindow *>(window->handle());
+ if (platformWindow->nativeViewId() == windowId)
+ platformWindow->onSurfaceChanged(surface);
+ }
+}
+
+void QAndroidPlatformWindow::windowFocusChanged(JNIEnv *env, jobject object,
+ jboolean focus, jint windowId)
+{
+ Q_UNUSED(env)
+ Q_UNUSED(object)
+ QWindow* window = QtAndroid::windowFromId(windowId);
+ Q_ASSERT_X(window, "QAndroidPlatformWindow", "windowFocusChanged event window should exist");
+ if (focus) {
+ QWindowSystemInterface::handleFocusWindowChanged(window);
+ } else if (!focus && window == qGuiApp->focusWindow()) {
+ // Clear focus if current window has lost focus
+ QWindowSystemInterface::handleFocusWindowChanged(nullptr);
+ }
+}
+
+bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
+{
+ if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
+ {
+ Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
+ Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow)
+ })) {
+ qCCritical(lcQpaWindow) << "RegisterNatives failed for"
+ << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
+ return false;
+ }
+ return true;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h
index f83ad7bea3..3f1e8ac992 100644
--- a/src/plugins/platforms/android/qandroidplatformwindow.h
+++ b/src/plugins/platforms/android/qandroidplatformwindow.h
@@ -1,59 +1,38 @@
-/****************************************************************************
-**
-** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDPLATFORMWINDOW_H
#define ANDROIDPLATFORMWINDOW_H
#include <qobject.h>
#include <qrect.h>
#include <qpa/qplatformwindow.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qjnitypes.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <jni.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
+Q_DECLARE_JNI_CLASS(QtWindow, "org/qtproject/qt/android/QtWindow")
+Q_DECLARE_JNI_CLASS(Surface, "android/view/Surface")
+
class QAndroidPlatformScreen;
-class QAndroidPlatformBackingStore;
class QAndroidPlatformWindow: public QPlatformWindow
{
public:
- explicit QAndroidPlatformWindow(QWindow *window);
+ enum class SurfaceContainer {
+ SurfaceView,
+ TextureView
+ };
+ explicit QAndroidPlatformWindow(QWindow *window);
+ ~QAndroidPlatformWindow();
void lower() override;
void raise() override;
@@ -63,7 +42,8 @@ public:
void setWindowFlags(Qt::WindowFlags flags) override;
Qt::WindowFlags windowFlags() const;
void setParent(const QPlatformWindow *window) override;
- WId winId() const override { return m_windowId; }
+
+ WId winId() const override;
bool setMouseGrabEnabled(bool grab) override { Q_UNUSED(grab); return false; }
bool setKeyboardGrabEnabled(bool grab) override { Q_UNUSED(grab); return false; }
@@ -75,32 +55,46 @@ public:
void propagateSizeHints() override;
void requestActivateWindow() override;
void updateSystemUiVisibility();
- inline bool isRaster() const {
- if (isForeignWindow())
- return false;
-
- return window()->surfaceType() == QSurface::RasterSurface
- || window()->surfaceType() == QSurface::RasterGLSurface;
- }
+ inline bool isRaster() const { return m_isRaster; }
bool isExposed() const override;
+ QtJniTypes::QtWindow nativeWindow() const { return m_nativeQtWindow; }
virtual void applicationStateChanged(Qt::ApplicationState);
+ int nativeViewId() const { return m_nativeViewId; }
- void setBackingStore(QAndroidPlatformBackingStore *store) { m_backingStore = store; }
- QAndroidPlatformBackingStore *backingStore() const { return m_backingStore; }
-
- virtual void repaint(const QRegion &) { }
+ static bool registerNatives(QJniEnvironment &env);
+ void onSurfaceChanged(QtJniTypes::Surface surface);
protected:
void setGeometry(const QRect &rect) override;
+ void lockSurface() { m_surfaceMutex.lock(); }
+ void unlockSurface() { m_surfaceMutex.unlock(); }
+ void createSurface();
+ void destroySurface();
+ void setNativeGeometry(const QRect &geometry);
+ void sendExpose() const;
+ bool blockedByModal() const;
+ bool isEmbeddingContainer() const;
-protected:
Qt::WindowFlags m_windowFlags;
Qt::WindowStates m_windowState;
-
- WId m_windowId;
-
- QAndroidPlatformBackingStore *m_backingStore = nullptr;
+ bool m_isRaster;
+
+ int m_nativeViewId = -1;
+ QtJniTypes::QtWindow m_nativeQtWindow;
+ SurfaceContainer m_surfaceContainerType = SurfaceContainer::SurfaceView;
+ QtJniTypes::QtWindow m_nativeParentQtWindow;
+ // The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex
+ QtJniTypes::Surface m_androidSurfaceObject;
+ QWaitCondition m_surfaceWaitCondition;
+ bool m_surfaceCreated = false;
+ QMutex m_surfaceMutex;
+
+private:
+ static void setSurface(JNIEnv *env, jobject obj, jint windowId, QtJniTypes::Surface surface);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(setSurface)
+ static void windowFocusChanged(JNIEnv *env, jobject object, jboolean focus, jint windowId);
+ Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(windowFocusChanged)
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/android/qandroidsystemlocale.cpp b/src/plugins/platforms/android/qandroidsystemlocale.cpp
index 351274a560..c5f2e59097 100644
--- a/src/plugins/platforms/android/qandroidsystemlocale.cpp
+++ b/src/plugins/platforms/android/qandroidsystemlocale.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidjnimain.h"
#include "qandroidsystemlocale.h"
@@ -48,34 +12,38 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_CLASS(Locale, "java/util/Locale")
+Q_DECLARE_JNI_CLASS(Resources, "android/content/res/Resources")
+Q_DECLARE_JNI_CLASS(Configuration, "android/content/res/Configuration")
+Q_DECLARE_JNI_CLASS(LocaleList, "android/os/LocaleList")
+
+using namespace QtJniTypes;
+
QAndroidSystemLocale::QAndroidSystemLocale() : m_locale(QLocale::C)
{
}
void QAndroidSystemLocale::getLocaleFromJava() const
{
- QWriteLocker locker(&m_lock);
-
- QJniObject javaLocaleObject;
- QJniObject javaActivity(QtAndroid::activity());
- if (!javaActivity.isValid())
- javaActivity = QtAndroid::service();
- if (javaActivity.isValid()) {
- QJniObject resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;");
- QJniObject configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;");
-
- javaLocaleObject = configuration.getObjectField("locale", "Ljava/util/Locale;");
- } else {
- javaLocaleObject = QJniObject::callStaticObjectMethod("java/util/Locale", "getDefault", "()Ljava/util/Locale;");
- }
+ const Locale javaLocaleObject = []{
+ const QJniObject javaContext = QtAndroidPrivate::context();
+ if (javaContext.isValid()) {
+ const QJniObject resources = javaContext.callMethod<Resources>("getResources");
+ const QJniObject configuration = resources.callMethod<Configuration>("getConfiguration");
+ return configuration.getField<Locale>("locale");
+ } else {
+ return Locale::callStaticMethod<Locale>("getDefault");
+ }
+ }();
- QString languageCode = javaLocaleObject.callObjectMethod("getLanguage", "()Ljava/lang/String;").toString();
- QString countryCode = javaLocaleObject.callObjectMethod("getCountry", "()Ljava/lang/String;").toString();
+ const QString languageCode = javaLocaleObject.callMethod<QString>("getLanguage");
+ const QString countryCode = javaLocaleObject.callMethod<QString>("getCountry");
- m_locale = QLocale(languageCode + QLatin1Char('_') + countryCode);
+ QWriteLocker locker(&m_lock);
+ m_locale = QLocale(languageCode + u'_' + countryCode);
}
-QVariant QAndroidSystemLocale::query(QueryType type, QVariant in) const
+QVariant QAndroidSystemLocale::query(QueryType type, QVariant &&in) const
{
if (type == LocaleChanged) {
getLocaleFromJava();
@@ -105,14 +73,26 @@ QVariant QAndroidSystemLocale::query(QueryType type, QVariant in) const
return m_locale.dayName(in.toInt(), QLocale::LongFormat);
case DayNameShort:
return m_locale.dayName(in.toInt(), QLocale::ShortFormat);
+ case DayNameNarrow:
+ return m_locale.dayName(in.toInt(), QLocale::NarrowFormat);
+ case StandaloneDayNameLong:
+ return m_locale.standaloneDayName(in.toInt(), QLocale::LongFormat);
+ case StandaloneDayNameShort:
+ return m_locale.standaloneDayName(in.toInt(), QLocale::ShortFormat);
+ case StandaloneDayNameNarrow:
+ return m_locale.standaloneDayName(in.toInt(), QLocale::NarrowFormat);
case MonthNameLong:
return m_locale.monthName(in.toInt(), QLocale::LongFormat);
case MonthNameShort:
return m_locale.monthName(in.toInt(), QLocale::ShortFormat);
+ case MonthNameNarrow:
+ return m_locale.monthName(in.toInt(), QLocale::NarrowFormat);
case StandaloneMonthNameLong:
return m_locale.standaloneMonthName(in.toInt(), QLocale::LongFormat);
case StandaloneMonthNameShort:
return m_locale.standaloneMonthName(in.toInt(), QLocale::ShortFormat);
+ case StandaloneMonthNameNarrow:
+ return m_locale.standaloneMonthName(in.toInt(), QLocale::NarrowFormat);
case DateToStringLong:
return m_locale.toString(in.toDate(), QLocale::LongFormat);
case DateToStringShort:
@@ -166,12 +146,9 @@ QVariant QAndroidSystemLocale::query(QueryType type, QVariant in) const
Q_ASSERT_X(false, Q_FUNC_INFO, "This can't happen.");
case UILanguages: {
if (QtAndroidPrivate::androidSdkVersion() >= 24) {
- QJniObject localeListObject =
- QJniObject::callStaticObjectMethod("android/os/LocaleList", "getDefault",
- "()Landroid/os/LocaleList;");
+ LocaleList localeListObject = LocaleList::callStaticMethod<LocaleList>("getDefault");
if (localeListObject.isValid()) {
- QString lang = localeListObject.callObjectMethod("toLanguageTags",
- "()Ljava/lang/String;").toString();
+ QString lang = localeListObject.callMethod<QString>("toLanguageTags");
// Some devices return with it enclosed in []'s so check if both exists before
// removing to ensure it is formatted correctly
if (lang.startsWith(QChar('[')) && lang.endsWith(QChar(']')))
diff --git a/src/plugins/platforms/android/qandroidsystemlocale.h b/src/plugins/platforms/android/qandroidsystemlocale.h
index 0937f6c134..cd37b48270 100644
--- a/src/plugins/platforms/android/qandroidsystemlocale.h
+++ b/src/plugins/platforms/android/qandroidsystemlocale.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QANDROIDSYSTEMLOCALE_H
#define QANDROIDSYSTEMLOCALE_H
@@ -47,10 +11,11 @@ QT_BEGIN_NAMESPACE
class QAndroidSystemLocale : public QSystemLocale
{
+ Q_DISABLE_COPY_MOVE(QAndroidSystemLocale)
public:
QAndroidSystemLocale();
- QVariant query(QueryType type, QVariant in) const override;
+ QVariant query(QueryType type, QVariant &&in) const override;
QLocale fallbackLocale() const override;
private: