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.txt36
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.cpp878
-rw-r--r--src/plugins/platforms/android/androidcontentfileengine.h125
-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.cpp377
-rw-r--r--src/plugins/platforms/android/androidjniinput.h67
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp581
-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.cpp75
-rw-r--r--src/plugins/platforms/android/androidwindowembedding.h41
-rw-r--r--src/plugins/platforms/android/extract-dummy.cpp40
-rw-r--r--src/plugins/platforms/android/extract.cpp42
-rw-r--r--src/plugins/platforms/android/main.cpp44
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp209
-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.cpp254
-rw-r--r--src/plugins/platforms/android/qandroidinputcontext.h50
-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.cpp91
-rw-r--r--src/plugins/platforms/android/qandroidplatformfiledialoghelper.h48
-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.cpp253
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.h72
-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.cpp480
-rw-r--r--src/plugins/platforms/android/qandroidplatformscreen.h90
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.cpp97
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.h40
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.cpp238
-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.cpp87
-rw-r--r--src/plugins/platforms/android/qandroidsystemlocale.h43
71 files changed, 4107 insertions, 4316 deletions
diff --git a/src/plugins/platforms/android/CMakeLists.txt b/src/plugins/platforms/android/CMakeLists.txt
index 416e25c28a..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
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES android # special case
+ 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,8 +40,19 @@ 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}
${QtBase_SOURCE_DIR}/src/3rdparty/android
@@ -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 b18c81e896..db6c601f33 100644
--- a/src/plugins/platforms/android/androidcontentfileengine.cpp
+++ b/src/plugins/platforms/android/androidcontentfileengine.cpp
@@ -1,57 +1,44 @@
-/****************************************************************************
-**
-** 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,
@@ -60,63 +47,183 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode,
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;
@@ -131,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)
{
}
@@ -174,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 531d0f80ff..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, 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 d4b51d38ba..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,25 +19,92 @@
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,
@@ -80,39 +113,33 @@ namespace QtAndroidInput
void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType)
{
- QJniObject::callStaticMethod<void>(applicationClass(),
- "showSoftwareKeyboard",
- "(IIIIII)V",
+ qtInputDelegate().callMethod<void>("showSoftwareKeyboard",
+ QtAndroidPrivate::activity(),
+ qtLayout().object<QtJniTypes::QtLayout>(),
left,
top,
width,
height,
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()
@@ -122,81 +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);
}
- static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y)
+ // 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)
+ {
+ 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, 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)
@@ -207,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);
}
@@ -254,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)
@@ -290,31 +386,35 @@ namespace QtAndroidInput
return touchDevice;
}
- static void touchEnd(JNIEnv * /*env*/, jobject /*thiz*/, jint /*winId*/, jint /*action*/)
+ static void touchEnd(JNIEnv * /*env*/, jobject /*thiz*/, jint winId, jint /*action*/)
{
if (m_touchPoints.isEmpty())
return;
QMutexLocker lock(QtAndroid::platformInterfaceMutex());
- QPointingDevice *touchDevice = getTouchDevice();
+ const QPointingDevice *touchDevice = getTouchDevice();
if (!touchDevice)
return;
- QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
+ QWindow *window = QtAndroid::windowFromId(winId);
+ if (!window)
+ return;
QWindowSystemInterface::handleTouchEvent(window, touchDevice, m_touchPoints);
}
- static void touchCancel(JNIEnv * /*env*/, jobject /*thiz*/, jint /*winId*/)
+ static void touchCancel(JNIEnv * /*env*/, jobject /*thiz*/, jint winId)
{
if (m_touchPoints.isEmpty())
return;
QMutexLocker lock(QtAndroid::platformInterfaceMutex());
- QPointingDevice *touchDevice = getTouchDevice();
+ const QPointingDevice *touchDevice = getTouchDevice();
if (!touchDevice)
return;
- QWindow *window = QtAndroid::topLevelWindowAt(m_touchPoints.at(0).area.center().toPoint());
+ QWindow *window = QtAndroid::windowFromId(winId);
+ if (!window)
+ return;
QWindowSystemInterface::handleTouchCancelEvent(window, touchDevice);
}
@@ -327,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
@@ -354,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;
@@ -365,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)
@@ -833,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)
@@ -848,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,
@@ -865,13 +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},
{"touchCancel", "(I)V", (void *)touchCancel},
- {"mouseDown", "(III)V", (void *)mouseDown},
- {"mouseUp", "(III)V", (void *)mouseUp},
+ {"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},
@@ -881,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 30c083f51c..28a2665bf6 100644
--- a/src/plugins/platforms/android/androidjniinput.h
+++ b/src/plugins/platforms/android/androidjniinput.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) 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
@@ -62,7 +31,27 @@ namespace QtAndroidInput
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 e99dca17a2..960f8ffc9c 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,14 +10,16 @@
#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>
@@ -63,13 +29,18 @@
#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;
@@ -79,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;
@@ -102,10 +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 QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
@@ -123,7 +91,9 @@ 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\"";
-static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+Q_DECLARE_JNI_CLASS(QtEmbeddedDelegateFactory, "org/qtproject/qt/android/QtEmbeddedDelegateFactory")
namespace QtAndroid
{
@@ -135,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)) {
@@ -160,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;
@@ -195,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()
+ {
+ if (!m_inputDelegate.isValid()) {
+ m_inputDelegate = qtActivityDelegate().callMethod<QtJniTypes::QtInputDelegate>(
+ "getInputDelegate");
+ }
+
+ return m_inputDelegate;
+ }
+
+ bool isQtApplication()
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility));
+ // 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 notifyAccessibilityLocationChange()
+ void notifyAccessibilityLocationChange(uint accessibilityObjectId)
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange");
+ qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId);
}
- void notifyObjectHide(uint accessibilityObjectId)
+ void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId);
+ 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 notifyScrolledEvent(uint accessibilityObjectId)
+ {
+ qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId);
}
- void notifyQtAndroidPluginRunning(bool running)
+ void notifyNativePluginIntegrationReady(bool ready)
{
- QJniObject::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running);
+ QJniObject::callStaticMethod<void>(m_applicationClass,
+ "notifyNativePluginIntegrationReady",
+ ready);
}
jobject createBitmap(QImage img, JNIEnv *env)
@@ -236,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,
@@ -321,63 +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;
- }
-
- jint generateViewId()
- {
- return QJniObject::callStaticMethod<jint>("android/view/View", "generateViewId", "()I");
- }
-
- int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
- {
- QJniEnvironment env;
- if (!env.jniEnv())
- return -1;
-
- m_surfacesMutex.lock();
- jint surfaceId = generateViewId();
- 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();
- jint surfaceId = generateViewId();
- 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)
@@ -389,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");
@@ -465,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());
@@ -527,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())
QtAndroidPrivate::waitForServiceSetup();
}
@@ -544,16 +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;
startQtAndroidPluginCalled.fetchAndAddRelease(1);
- int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
+ const int ret = m_main(argc, argv.data());
+ qInfo() << "main() returned" << ret;
if (m_mainLibraryHnd) {
int res = dlclose(m_mainLibraryHnd);
@@ -561,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);
@@ -609,10 +527,6 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
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)
@@ -623,51 +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)
-{
- QMutexLocker lock(&m_surfacesMutex);
- const auto &it = m_surfaces.find(id);
- if (it == m_surfaces.end())
- return;
-
- 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, jfloat refreshRate)
{
+ Q_UNUSED(availableLeftPixels)
+ Q_UNUSED(availableTopPixels)
+
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);
+ 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*/)
{
@@ -687,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)
@@ -760,17 +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,
@@ -791,119 +733,137 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
}
static JNINativeMethod methods[] = {
- { "startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z",
- (void *)startQtAndroidPlugin },
+ { "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 },
- { "setDisplayMetrics", "(IIIIIIDDDDF)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 },
- { "handleRefreshRateChanged", "(F)V", (void *)handleRefreshRateChanged }
+ { "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
@@ -916,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..230776f571
--- /dev/null
+++ b/src/plugins/platforms/android/androidwindowembedding.cpp
@@ -0,0 +1,75 @@
+// 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");
+Q_DECLARE_JNI_CLASS(QtEmbeddedDelegate, "org/qtproject/qt/android/QtEmbeddedDelegate");
+
+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) {
+ using namespace QtJniTypes;
+ bool success = env.registerNativeMethods(Traits<QtEmbeddedDelegate>::className(),
+ {Q_JNI_NATIVE_SCOPED_METHOD(createRootWindow, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(deleteWindow, QtAndroidWindowEmbedding)});
+
+ success &= env.registerNativeMethods(Traits<QtView>::className(),
+ {Q_JNI_NATIVE_SCOPED_METHOD(setWindowVisible, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(resizeWindow, QtAndroidWindowEmbedding)});
+ return success;
+
+ }
+}
+
+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 43c03792d4..a8b27bb0d8 100644
--- a/src/plugins/platforms/android/extract-dummy.cpp
+++ b/src/plugins/platforms/android/extract-dummy.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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 <jni.h>
#include <extract.h>
diff --git a/src/plugins/platforms/android/extract.cpp b/src/plugins/platforms/android/extract.cpp
index 4566d2ac69..d81d70d18d 100644
--- a/src/plugins/platforms/android/extract.cpp
+++ b/src/plugins/platforms/android/extract.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** 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
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 0c6fb92ffb..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
+ bool advance()
{
- return !empty() && m_index + 1 < size();
- }
-
- std::optional<std::pair<QString, AssetItem>> next()
- {
- 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
+ bool advance() override
{
- if (!m_currentIterator)
- return false;
- return m_currentIterator->hasNext();
- }
-
- QString next() override
- {
- if (!m_currentIterator)
- return {};
- auto res = m_currentIterator->next();
- if (!res)
- return {};
- return res->first;
+ return m_currentIterator ? m_currentIterator->advance() : false;
}
private:
@@ -265,7 +218,7 @@ public:
{
Q_UNUSED(permissions);
- if (m_isFolder || (openMode & QIODevice::WriteOnly))
+ 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);
@@ -279,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;
}
@@ -320,30 +272,32 @@ public:
{
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);
@@ -358,22 +312,51 @@ public:
return;
close();
m_fileName = cleanedAssetPath(file);
- switch (FolderIterator::fileType(m_fileName)) {
- case AssetItem::Type::File:
- open(QIODevice::ReadOnly, std::nullopt);
- 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;
}
@@ -381,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 417f3469a8..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>
@@ -116,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;
@@ -129,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();});
@@ -149,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;
@@ -162,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;
@@ -175,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;
@@ -201,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);
@@ -226,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()));
@@ -241,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()));
}
@@ -254,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()));
}
@@ -270,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;
@@ -283,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;
@@ -297,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;
@@ -311,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;
@@ -324,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;
@@ -337,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;
@@ -350,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;
@@ -363,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;
@@ -376,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},
@@ -404,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()
@@ -421,6 +363,7 @@ QAndroidInputContext::QAndroidInputContext()
, m_handleMode(Hidden)
, m_batchEditNestingLevel(0)
, m_focusObject(0)
+ , m_fullScreenMode(false)
{
QJniEnvironment env;
jclass clazz = env.findClass(QtNativeInputConnectionClassName);
@@ -603,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
@@ -622,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;
@@ -629,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;
}
@@ -654,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;
+
+ int rightSideOfScreen = platformIntegration->screen()->availableGeometry().right();
+ if (leftPoint.x() < m_selectHandleWidth)
+ leftPoint.setX(m_selectHandleWidth);
- if (rightPoint.x() > rightSideOfScreen - m_selectHandleWidth)
- rightPoint.setX(rightSideOfScreen - m_selectHandleWidth);
+ if (rightPoint.x() > rightSideOfScreen - m_selectHandleWidth)
+ rightPoint.setX(rightSideOfScreen - m_selectHandleWidth);
- QPoint editPoint(qPlatformWindow->mapToGlobal(leftRect.united(rightRect).topLeft().toPoint()));
- uint32_t buttons = readOnly ? EditContext::CopyButton | EditContext::SelectAllButton
- : EditContext::AllButtons;
+ 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();
+ QtAndroidInput::updateHandles(m_handleMode, editPoint, buttons, leftPoint, rightPoint,
+ query.value(Qt::ImCurrentSelection).toString().isRightToLeft());
+ m_hideCursorHandleTimer.stop();
+ }
}
/*
@@ -809,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();
}
}
@@ -916,10 +903,15 @@ void QAndroidInputContext::showInputPanel()
if (query.isNull())
return;
+ if (!qGuiApp->focusWindow()->handle())
+ return; // not a real window, probably VR/XR
+
disconnect(m_updateCursorPosConnection);
+ m_updateCursorPosConnection = {};
+
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
+ else if (qGuiApp->focusObject()->metaObject()->indexOfSignal("cursorPositionChanged()") >= 0)
m_updateCursorPosConnection = connect(qGuiApp->focusObject(), SIGNAL(cursorPositionChanged()), this, SLOT(updateCursorPosition()));
QRect rect = screenInputItemRectangle();
@@ -975,7 +967,6 @@ void QAndroidInputContext::setFocusObject(QObject *object)
m_focusObject = object;
reset();
}
- QPlatformInputContext::setFocusObject(object);
updateSelectionHandles();
}
@@ -1132,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;
@@ -1143,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();
@@ -1226,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();
@@ -1446,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;
@@ -1554,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 e9bfb98e66..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,6 +100,8 @@ 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);
@@ -149,6 +117,7 @@ private slots:
void showInputPanelLater(Qt::ApplicationState);
private:
+ bool isImhNoTextHandlesSet();
void sendInputMethodEvent(QInputMethodEvent *event);
QSharedPointer<QInputMethodQueryEvent> focusObjectInputMethodQuery(Qt::InputMethodQueries queries = Qt::ImQueryAll);
bool focusObjectIsComposing() const;
@@ -163,8 +132,9 @@ private:
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 1ec5867a38..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"
@@ -50,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace QtAndroidFileDialogHelper {
#define RESULT_OK -1
@@ -59,7 +25,7 @@ const char JniIntentClass[] = "android/content/Intent";
QAndroidPlatformFileDialogHelper::QAndroidPlatformFileDialogHelper()
: QPlatformFileDialogHelper(),
- m_activity(QtAndroid::activity())
+ m_activity(QtAndroidPrivate::activity())
{
}
@@ -79,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;
@@ -98,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();
}
@@ -129,6 +97,22 @@ void QAndroidPlatformFileDialogHelper::setInitialFileName(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(
@@ -162,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());
@@ -211,11 +198,8 @@ 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) {
- // TODO: The initial folder to show at the start should be handled by EXTRA_INITIAL_URI
- // Take only the file name.
+ if (selectedFiles.size() > 0)
setInitialFileName(selectedFiles.first().fileName());
- }
} else if (options()->acceptMode() == QFileDialogOptions::AcceptOpen) {
switch (options()->fileMode()) {
case QFileDialogOptions::FileMode::DirectoryOnly:
@@ -239,6 +223,8 @@ bool QAndroidPlatformFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::Win
setMimeTypes();
}
+ setInitialDirectoryUri(m_directory.toString());
+
QtAndroidPrivate::registerActivityResultListener(this);
m_activity.callMethod<void>("startActivityForResult", "(Landroid/content/Intent;I)V",
m_intent.object(), REQUEST_CODE);
@@ -252,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 f573e72fef..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,8 +32,8 @@ 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;
@@ -77,12 +41,14 @@ private:
QJniObject getFileDialogIntent(const QString &intentType);
void takePersistableUriPermission(const QJniObject &uri);
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 8f2b7970f1..4d9e6fa704 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,48 @@
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);
+}
+
+} // 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())
@@ -165,21 +153,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
{
@@ -197,11 +181,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();
@@ -214,13 +219,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;");
@@ -260,6 +265,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;");
@@ -287,19 +296,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
@@ -307,13 +316,16 @@ 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 QtAndroidPrivate::activity().isValid();
+ case ForeignWindows: return QtAndroidPrivate::activity().isValid();
+ case ThreadedOpenGL: return !needsBasicRenderloopWorkaround() && QtAndroidPrivate::activity().isValid();
+ 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);
}
@@ -321,15 +333,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 (!QtAndroidPrivate::activity().isValid())
return nullptr;
QSurfaceFormat format(context->format());
format.setAlphaBufferSize(8);
@@ -347,7 +359,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());
@@ -361,7 +373,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;
@@ -372,7 +384,7 @@ QOffscreenSurface *QAndroidPlatformIntegration::createOffscreenSurface(ANativeWi
QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const
{
- if (!QtAndroid::activity())
+ if (!QtAndroidPrivate::activity().isValid())
return nullptr;
#if QT_CONFIG(vulkan)
@@ -458,7 +470,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));
@@ -467,7 +479,7 @@ 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;
}
@@ -492,12 +504,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;
@@ -522,12 +535,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 4fffed651b..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>
@@ -99,11 +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
@@ -112,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
@@ -133,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
@@ -145,6 +124,8 @@ private:
QThread *m_mainThread;
+ static Qt::ColorScheme m_colorScheme;
+
static QRect m_defaultAvailableGeometry;
static QSize m_defaultPhysicalSize;
static QSize m_defaultScreenSize;
@@ -157,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 59c8f396af..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"
@@ -88,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) {
@@ -102,69 +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);
- QJniObject activity(QtAndroid::activity());
- if (!activity.isValid())
- return;
- QJniObject display;
- if (QNativeInterface::QAndroidApplication::sdkVersion() < 30) {
- display = activity.callObjectMethod("getWindowManager", "()Landroid/view/WindowManager;")
- .callObjectMethod("getDefaultDisplay", "()Landroid/view/Display;");
- } else {
- display = activity.callObjectMethod("getDisplay", "()Landroid/view/Display;");
- }
- if (!display.isValid())
- return;
-
- m_name = display.callObjectMethod("getName", "()Ljava/lang/String;").toString();
- m_refreshRate = display.callMethod<jfloat>("getRefreshRate");
+ connect(qGuiApp, &QGuiApplication::applicationStateChanged, this,
+ &QAndroidPlatformScreen::applicationStateChanged);
- if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
- m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
+ if (!displayObject.isValid())
return;
- }
- QJniEnvironment env;
- const jint currentMode = display.callObjectMethod("getMode", "()Landroid/view/Display$Mode;")
- .callMethod<jint>("getModeId");
- const auto modes = display.callObjectMethod("getSupportedModes",
- "()[Landroid/view/Display$Mode;");
- const auto modesArray = jobjectArray(modes.object());
- const auto sz = env->GetArrayLength(modesArray);
- for (jsize i = 0; i < sz; ++i) {
- auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modesArray, i));
- if (currentMode == mode.callMethod<jint>("getModeId"))
- m_currentMode = m_modes.size();
- m_modes << Mode { .size = QSize { mode.callMethod<jint>("getPhysicalHeight"),
- mode.callMethod<jint>("getPhysicalWidth") },
- .refreshRate = mode.callMethod<jfloat>("getRefreshRate") };
+ 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")
+ };
+ }
}
-
- if (m_modes.isEmpty())
- m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
}
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
@@ -176,100 +153,54 @@ 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, Qt::ActiveWindowFocusReason);
- 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";
- QWindow *w = topWindow();
- QWindowSystemInterface::handleWindowActivated(w, Qt::ActiveWindowFocusReason);
- topWindowChanged(w);
+ QtAndroid::qtActivityDelegate().callMethod<void>("removeTopLevelWindow", window->nativeViewId());
+
+ 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, Qt::ActiveWindowFocusReason);
- 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, Qt::ActiveWindowFocusReason);
- topWindowChanged(w);
-}
+ QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToBack", window->nativeViewId());
-void QAndroidPlatformScreen::scheduleUpdate()
-{
- if (!m_updatePending) {
- m_updatePending = true;
- QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
- }
-}
-
-void QAndroidPlatformScreen::setDirty(const QRect &rect)
-{
- QRect intersection = rect.intersected(m_availableGeometry);
- m_dirtyRect |= intersection;
- scheduleUpdate();
+ topVisibleWindowChanged();
}
void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
@@ -283,6 +214,29 @@ void QAndroidPlatformScreen::setSize(const QSize &size)
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry());
}
+void QAndroidPlatformScreen::setSizeParameters(const QSize &physicalSize, const QSize &size,
+ const QRect &availableGeometry)
+{
+ // 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());
+ }
+}
+
+int QAndroidPlatformScreen::displayId() const
+{
+ return m_displayId;
+}
+
void QAndroidPlatformScreen::setRefreshRate(qreal refreshRate)
{
if (refreshRate == m_refreshRate)
@@ -291,9 +245,13 @@ void QAndroidPlatformScreen::setRefreshRate(qreal refreshRate)
QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), refreshRate);
}
+void QAndroidPlatformScreen::setOrientation(Qt::ScreenOrientation orientation)
+{
+ QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), orientation);
+}
+
void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
{
- QMutexLocker lock(&m_surfaceMutex);
if (m_availableGeometry == rect)
return;
@@ -314,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);
}
@@ -495,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 e0de85277a..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); }
@@ -74,35 +38,28 @@ public:
int currentMode() const override { return m_currentMode; }
int preferredMode() const override { return m_currentMode; }
qreal refreshRate() const override { return m_refreshRate; }
-
- inline QWindow *topWindow() const;
+ 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;
@@ -111,31 +68,16 @@ protected:
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 8c2af5fff4..f43e7cdd6a 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.cpp
+++ b/src/plugins/platforms/android/qandroidplatformservices.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 "qandroidplatformservices.h"
@@ -50,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QAndroidPlatformServices::QAndroidPlatformServices()
{
m_actionView = QJniObject::getStaticObjectField("android/content/Intent", "ACTION_VIEW",
@@ -69,6 +35,11 @@ QAndroidPlatformServices::QAndroidPlatformServices()
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;
@@ -80,21 +51,45 @@ bool QAndroidPlatformServices::openUrl(const QUrl &theUrl)
// 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)
diff --git a/src/plugins/platforms/android/qandroidplatformservices.h b/src/plugins/platforms/android/qandroidplatformservices.h
index dff132d56d..762d14deeb 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.h
+++ b/src/plugins/platforms/android/qandroidplatformservices.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 ANDROIDPLATFORMDESKTOPSERVICE_H
#define ANDROIDPLATFORMDESKTOPSERVICE_H
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp
index c79b1836bc..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,21 +156,17 @@ static void setPaletteColor(const QVariantMap &object,
QJsonObject AndroidStyle::loadStyleData()
{
- QString stylePath(QLatin1String(qgetenv("ANDROID_STYLE_PATH")));
+ QString stylePath(QLatin1StringView(qgetenv("ANDROID_STYLE_PATH")));
const QLatin1Char slashChar('/');
if (!stylePath.isEmpty() && !stylePath.endsWith(slashChar))
stylePath += slashChar;
- Q_ASSERT(!stylePath.isEmpty());
+ if (QAndroidPlatformIntegration::colorScheme() == Qt::ColorScheme::Dark)
+ stylePath += "darkUiMode/"_L1;
- QString androidTheme = QLatin1String(qgetenv("QT_ANDROID_THEME"));
- if (!androidTheme.isEmpty() && !androidTheme.endsWith(slashChar))
- androidTheme += slashChar;
-
- if (!androidTheme.isEmpty() && QFileInfo::exists(stylePath + androidTheme + QLatin1String("style.json")))
- stylePath += androidTheme;
+ Q_ASSERT(!stylePath.isEmpty());
- QFile f(stylePath + QLatin1String("style.json"));
+ QFile f(stylePath + "style.json"_L1);
if (!f.open(QIODevice::ReadOnly))
return QJsonObject();
@@ -219,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();
@@ -237,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.)
@@ -249,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);
@@ -262,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())) {
@@ -294,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())));
@@ -322,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));
@@ -343,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);
@@ -359,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) {
@@ -452,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 74a549454d..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();
@@ -178,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: