diff options
Diffstat (limited to 'src/plugins/platforms/android/qandroidplatformintegration.cpp')
-rw-r--r-- | src/plugins/platforms/android/qandroidplatformintegration.cpp | 293 |
1 files changed, 198 insertions, 95 deletions
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 7b4102fac6..6efd3fc631 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qandroidplatformintegration.h" @@ -45,7 +9,6 @@ #include "qabstracteventdispatcher.h" #include "qandroideventdispatcher.h" #include "qandroidplatformaccessibility.h" -#include "qandroidplatformbackingstore.h" #include "qandroidplatformclipboard.h" #include "qandroidplatformfontdatabase.h" #include "qandroidplatformforeignwindow.h" @@ -65,6 +28,7 @@ #include <QtGui/private/qeglpbuffer_p.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qoffscreensurface_p.h> +#include <QtGui/private/qrhibackingstore_p.h> #include <qpa/qplatformoffscreensurface.h> #include <qpa/qplatformwindow.h> #include <qpa/qwindowsysteminterface.h> @@ -80,24 +44,54 @@ QT_BEGIN_NAMESPACE -QSize QAndroidPlatformIntegration::m_defaultScreenSize = QSize(320, 455); -QRect QAndroidPlatformIntegration::m_defaultAvailableGeometry = QRect(0, 0, 320, 455); -QSize QAndroidPlatformIntegration::m_defaultPhysicalSize = QSize(50, 71); +using namespace Qt::StringLiterals; + +Q_CONSTINIT QSize QAndroidPlatformIntegration::m_defaultScreenSize = QSize(320, 455); +Q_CONSTINIT QRect QAndroidPlatformIntegration::m_defaultAvailableGeometry = QRect(0, 0, 320, 455); +Q_CONSTINIT QSize QAndroidPlatformIntegration::m_defaultPhysicalSize = QSize(50, 71); Qt::ScreenOrientation QAndroidPlatformIntegration::m_orientation = Qt::PrimaryOrientation; Qt::ScreenOrientation QAndroidPlatformIntegration::m_nativeOrientation = Qt::PrimaryOrientation; bool QAndroidPlatformIntegration::m_showPasswordEnabled = false; -static bool m_running = false; + +Q_DECLARE_JNI_CLASS(QtNative, "org/qtproject/qt/android/QtNative") +Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager") +Q_DECLARE_JNI_CLASS(Display, "android/view/Display") + +Q_DECLARE_JNI_CLASS(List, "java/util/List") + +namespace { + +QAndroidPlatformScreen* createScreenForDisplayId(int displayId) +{ + const QJniObject display = QtJniTypes::QtDisplayManager::callStaticMethod<QtJniTypes::Display>( + "getDisplay", QtAndroidPrivate::context(), displayId); + if (!display.isValid()) + return nullptr; + return new QAndroidPlatformScreen(display); +} + +static bool isValidAndroidContextForRendering() +{ + return QtAndroid::isQtApplication() ? QtAndroidPrivate::activity().isValid() + : QtAndroidPrivate::context().isValid(); +} + +} // anonymous namespace void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource) { if (resource=="JavaVM") return QtAndroid::javaVM(); - if (resource == "QtActivity") - return QtAndroid::activity(); - if (resource == "QtService") - return QtAndroid::service(); + if (resource == "QtActivity") { + extern Q_CORE_EXPORT jobject qt_androidActivity(); + return qt_androidActivity(); + } + if (resource == "QtService") { + extern Q_CORE_EXPORT jobject qt_androidService(); + return qt_androidService(); + } if (resource == "AndroidStyleData") { if (m_androidStyle) { if (m_androidStyle->m_styleData.isEmpty()) @@ -143,6 +137,19 @@ void *QAndroidPlatformNativeInterface::nativeResourceForWindow(const QByteArray return nullptr; } +void *QAndroidPlatformNativeInterface::nativeResourceForContext(const QByteArray &resource, QOpenGLContext *context) +{ + if (QEGLPlatformContext *platformContext = static_cast<QEGLPlatformContext *>(context->handle())) { + if (resource == "eglcontext") + return platformContext->eglContext(); + else if (resource == "eglconfig") + return platformContext->eglConfig(); + else if (resource == "egldisplay") + return platformContext->eglDisplay(); + } + return nullptr; +} + void QAndroidPlatformNativeInterface::customEvent(QEvent *event) { if (event->type() != QEvent::User) @@ -152,21 +159,17 @@ void QAndroidPlatformNativeInterface::customEvent(QEvent *event) QAndroidPlatformIntegration *api = static_cast<QAndroidPlatformIntegration *>(QGuiApplicationPrivate::platformIntegration()); QtAndroid::setAndroidPlatformIntegration(api); -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) // Android accessibility activation event might have been already received api->accessibility()->setActive(QtAndroidAccessibility::isActive()); -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) - if (!m_running) { - m_running = true; - QtAndroid::notifyQtAndroidPluginRunning(m_running); - } api->flushPendingUpdates(); } QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶mList) : m_touchDevice(nullptr) -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) , m_accessibility(nullptr) #endif { @@ -184,11 +187,32 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API))) qFatal("Could not bind GL_ES API"); - m_primaryScreen = new QAndroidPlatformScreen(); - QWindowSystemInterface::handleScreenAdded(m_primaryScreen); - m_primaryScreen->setPhysicalSize(m_defaultPhysicalSize); - m_primaryScreen->setSize(m_defaultScreenSize); - m_primaryScreen->setAvailableGeometry(m_defaultAvailableGeometry); + using namespace QtJniTypes; + m_primaryDisplayId = Display::getStaticField<jint>("DEFAULT_DISPLAY"); + const QJniObject nativeDisplaysList = QtDisplayManager::callStaticMethod<List>( + "getAvailableDisplays", QtAndroidPrivate::context()); + + const int numberOfAvailableDisplays = nativeDisplaysList.callMethod<jint>("size"); + for (int i = 0; i < numberOfAvailableDisplays; ++i) { + const QJniObject display = + nativeDisplaysList.callObjectMethod<jobject, jint>("get", jint(i)); + const int displayId = display.callMethod<jint>("getDisplayId"); + const bool isPrimary = (m_primaryDisplayId == displayId); + auto screen = new QAndroidPlatformScreen(display); + + if (isPrimary) + m_primaryScreen = screen; + + QWindowSystemInterface::handleScreenAdded(screen, isPrimary); + m_screens[displayId] = screen; + } + + if (numberOfAvailableDisplays == 0) { + // If no displays are found, add a dummy display + auto defaultScreen = new QAndroidPlatformScreen(QJniObject {}); + m_primaryScreen = defaultScreen; + QWindowSystemInterface::handleScreenAdded(defaultScreen, true); + } m_mainThread = QThread::currentThread(); @@ -201,13 +225,13 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ m_androidSystemLocale = new QAndroidSystemLocale; -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) m_accessibility = new QAndroidPlatformAccessibility(); -#endif // QT_NO_ACCESSIBILITY +#endif // QT_CONFIG(accessibility) - QJniObject javaActivity(QtAndroid::activity()); + QJniObject javaActivity = QtAndroidPrivate::activity(); if (!javaActivity.isValid()) - javaActivity = QtAndroid::service(); + javaActivity = QtAndroidPrivate::service(); if (javaActivity.isValid()) { QJniObject resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); @@ -247,6 +271,10 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ maxTouchPoints, 0); QWindowSystemInterface::registerInputDevice(m_touchDevice); + + QWindowSystemInterface::registerInputDevice( + new QInputDevice("Virtual keyboard"_L1, 0, QInputDevice::DeviceType::Keyboard, + {}, qApp)); } auto contentResolver = javaActivity.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;"); @@ -274,19 +302,19 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ static bool needsBasicRenderloopWorkaround() { static bool needsWorkaround = - QtAndroid::deviceName().compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0 - || QtAndroid::deviceName().compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0 - || QtAndroid::deviceName().compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0; + QtAndroid::deviceName().compare("samsung SM-T211"_L1, Qt::CaseInsensitive) == 0 + || QtAndroid::deviceName().compare("samsung SM-T210"_L1, Qt::CaseInsensitive) == 0 + || QtAndroid::deviceName().compare("samsung SM-T215"_L1, Qt::CaseInsensitive) == 0; return needsWorkaround; } void QAndroidPlatformIntegration::initialize() { - const QString icStr = QPlatformInputContextFactory::requested(); - if (icStr.isNull()) + const auto icStrs = QPlatformInputContextFactory::requested(); + if (icStrs.isEmpty()) m_inputContext.reset(new QAndroidInputContext); else - m_inputContext.reset(QPlatformInputContextFactory::create(icStr)); + m_inputContext.reset(QPlatformInputContextFactory::create(icStrs)); } bool QAndroidPlatformIntegration::hasCapability(Capability cap) const @@ -294,13 +322,19 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const switch (cap) { case ApplicationState: return true; case ThreadedPixmaps: return true; - case NativeWidgets: return QtAndroid::activity(); - case OpenGL: return QtAndroid::activity(); - case ForeignWindows: return QtAndroid::activity(); - case ThreadedOpenGL: return !needsBasicRenderloopWorkaround() && QtAndroid::activity(); - case RasterGLSurface: return QtAndroid::activity(); + case NativeWidgets: return QtAndroidPrivate::activity().isValid(); + case OpenGL: + return isValidAndroidContextForRendering(); + case ForeignWindows: + return isValidAndroidContextForRendering(); + case ThreadedOpenGL: + return !needsBasicRenderloopWorkaround() && isValidAndroidContextForRendering(); + case RasterGLSurface: return QtAndroidPrivate::activity().isValid(); case TopStackedNativeChildWindows: return false; case MaximizeUsingFullscreenGeometry: return true; + // FIXME QTBUG-118849 - we do not implement grabWindow() anymore, calling it will return + // a null QPixmap also for raster windows - for OpenGL windows this was always true + case ScreenWindowGrabbing: return false; default: return QPlatformIntegration::hasCapability(cap); } @@ -308,15 +342,15 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(QWindow *window) const { - if (!QtAndroid::activity()) + if (!QtAndroidPrivate::activity().isValid()) return nullptr; - return new QAndroidPlatformBackingStore(window); + return new QRhiBackingStore(window); } QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const { - if (!QtAndroid::activity()) + if (!isValidAndroidContextForRendering()) return nullptr; QSurfaceFormat format(context->format()); format.setAlphaBufferSize(8); @@ -334,7 +368,7 @@ QOpenGLContext *QAndroidPlatformIntegration::createOpenGLContext(EGLContext cont QPlatformOffscreenSurface *QAndroidPlatformIntegration::createPlatformOffscreenSurface(QOffscreenSurface *surface) const { - if (!QtAndroid::activity()) + if (!QtAndroidPrivate::activity().isValid()) return nullptr; QSurfaceFormat format(surface->requestedFormat()); @@ -348,7 +382,7 @@ QPlatformOffscreenSurface *QAndroidPlatformIntegration::createPlatformOffscreenS QOffscreenSurface *QAndroidPlatformIntegration::createOffscreenSurface(ANativeWindow *nativeSurface) const { - if (!QtAndroid::activity() || !nativeSurface) + if (!QtAndroidPrivate::activity().isValid() || !nativeSurface) return nullptr; auto *surface = new QOffscreenSurface; @@ -359,7 +393,7 @@ QOffscreenSurface *QAndroidPlatformIntegration::createOffscreenSurface(ANativeWi QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const { - if (!QtAndroid::activity()) + if (!isValidAndroidContextForRendering()) return nullptr; #if QT_CONFIG(vulkan) @@ -445,7 +479,7 @@ Qt::WindowState QAndroidPlatformIntegration::defaultWindowState(Qt::WindowFlags return QPlatformIntegration::defaultWindowState(flags); } -static const QLatin1String androidThemeName("android"); +static const auto androidThemeName = "android"_L1; QStringList QAndroidPlatformIntegration::themeNames() const { return QStringList(QString(androidThemeName)); @@ -454,19 +488,15 @@ QStringList QAndroidPlatformIntegration::themeNames() const QPlatformTheme *QAndroidPlatformIntegration::createPlatformTheme(const QString &name) const { if (androidThemeName == name) - return new QAndroidPlatformTheme(m_androidPlatformNativeInterface); + return QAndroidPlatformTheme::instance(m_androidPlatformNativeInterface); return 0; } -void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int availableLeft, - int availableTop, - int availableWidth, - int availableHeight, - int physicalWidth, - int physicalHeight, - int screenWidth, - int screenHeight) +void QAndroidPlatformIntegration::setDefaultDisplayMetrics(int availableLeft, int availableTop, + int availableWidth, int availableHeight, + int physicalWidth, int physicalHeight, + int screenWidth, int screenHeight) { m_defaultAvailableGeometry = QRect(availableLeft, availableTop, availableWidth, availableHeight); @@ -483,12 +513,13 @@ void QAndroidPlatformIntegration::setScreenOrientation(Qt::ScreenOrientation cur void QAndroidPlatformIntegration::flushPendingUpdates() { - m_primaryScreen->setPhysicalSize(m_defaultPhysicalSize); - m_primaryScreen->setSize(m_defaultScreenSize); - m_primaryScreen->setAvailableGeometry(m_defaultAvailableGeometry); + if (m_primaryScreen) { + m_primaryScreen->setSizeParameters(m_defaultPhysicalSize, m_defaultScreenSize, + m_defaultAvailableGeometry); + } } -#ifndef QT_NO_ACCESSIBILITY +#if QT_CONFIG(accessibility) QPlatformAccessibility *QAndroidPlatformIntegration::accessibility() const { return m_accessibility; @@ -513,6 +544,78 @@ void QAndroidPlatformIntegration::setScreenSize(int width, int height) QMetaObject::invokeMethod(m_primaryScreen, "setSize", Qt::AutoConnection, Q_ARG(QSize, QSize(width, height))); } +Qt::ColorScheme QAndroidPlatformIntegration::m_colorScheme = Qt::ColorScheme::Light; + +void QAndroidPlatformIntegration::updateColorScheme(Qt::ColorScheme colorScheme) +{ + if (m_colorScheme == colorScheme) + return; + m_colorScheme = colorScheme; + + QMetaObject::invokeMethod(qGuiApp, + [] () { QAndroidPlatformTheme::instance()->updateColorScheme();}); +} + +void QAndroidPlatformIntegration::setScreenSizeParameters(const QSize &physicalSize, + const QSize &screenSize, + const QRect &availableGeometry) +{ + if (m_primaryScreen) { + QMetaObject::invokeMethod(m_primaryScreen, "setSizeParameters", Qt::AutoConnection, + Q_ARG(QSize, physicalSize), Q_ARG(QSize, screenSize), + Q_ARG(QRect, availableGeometry)); + } +} + +void QAndroidPlatformIntegration::setRefreshRate(qreal refreshRate) +{ + if (m_primaryScreen) + QMetaObject::invokeMethod(m_primaryScreen, "setRefreshRate", Qt::AutoConnection, + Q_ARG(qreal, refreshRate)); +} + +void QAndroidPlatformIntegration::handleScreenAdded(int displayId) +{ + auto result = m_screens.insert(displayId, nullptr); + if (result.first->second == nullptr) { + auto it = result.first; + it->second = createScreenForDisplayId(displayId); + if (it->second == nullptr) + return; + const bool isPrimary = (m_primaryDisplayId == displayId); + if (isPrimary) + m_primaryScreen = it->second; + QWindowSystemInterface::handleScreenAdded(it->second, isPrimary); + } else { + qWarning() << "Display with id" << displayId << "already exists."; + } +} + +void QAndroidPlatformIntegration::handleScreenChanged(int displayId) +{ + auto it = m_screens.find(displayId); + if (it == m_screens.end() || it->second == nullptr) { + handleScreenAdded(displayId); + } + // We do not do anything more here as handling of change of + // rotation and refresh rate is done in QtActivityDelegate java class + // which calls QAndroidPlatformIntegration::setOrientation, and + // QAndroidPlatformIntegration::setRefreshRate accordingly. +} + +void QAndroidPlatformIntegration::handleScreenRemoved(int displayId) +{ + auto it = m_screens.find(displayId); + + if (it == m_screens.end()) + return; + + if (it->second != nullptr) + QWindowSystemInterface::handleScreenRemoved(it->second); + + m_screens.erase(it); +} + #if QT_CONFIG(vulkan) QPlatformVulkanInstance *QAndroidPlatformIntegration::createPlatformVulkanInstance(QVulkanInstance *instance) const |