diff options
Diffstat (limited to 'src/plugins/platforms/android/androidjnimain.cpp')
-rw-r--r-- | src/plugins/platforms/android/androidjnimain.cpp | 612 |
1 files changed, 285 insertions, 327 deletions
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 6dff19f3d5..5bd2b924fc 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include <dlfcn.h> #include <pthread.h> @@ -46,29 +10,37 @@ #include "androidcontentfileengine.h" #include "androiddeadlockprotector.h" #include "androidjniaccessibility.h" -#include "androidjniclipboard.h" #include "androidjniinput.h" #include "androidjnimain.h" #include "androidjnimenu.h" +#include "androidwindowembedding.h" #include "qandroidassetsfileenginehandler.h" #include "qandroideventdispatcher.h" #include "qandroidplatformdialoghelpers.h" #include "qandroidplatformintegration.h" +#include "qandroidplatformclipboard.h" +#include "qandroidplatformwindow.h" #include <android/api-level.h> #include <android/asset_manager_jni.h> #include <android/bitmap.h> #include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/qbasicatomic.h> #include <QtCore/qjnienvironment.h> #include <QtCore/qjniobject.h> +#include <QtCore/qprocess.h> #include <QtCore/qresource.h> +#include <QtCore/qscopeguard.h> #include <QtCore/qthread.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <qpa/qwindowsysteminterface.h> + +using namespace Qt::StringLiterals; + QT_BEGIN_NAMESPACE static JavaVM *m_javaVM = nullptr; @@ -78,11 +50,9 @@ 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 int m_pendingApplicationState = -1; static QBasicMutex m_platformMutex; @@ -101,11 +71,6 @@ static void *m_mainLibraryHnd = nullptr; static QList<QByteArray> m_applicationParams; static sem_t m_exitSemaphore, m_terminateSemaphore; -QHash<int, AndroidSurfaceClient *> m_surfaces; - -static QBasicMutex m_surfacesMutex; -static int m_surfaceId = 1; - static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr; @@ -117,12 +82,17 @@ static double m_density = 1.0; static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr; static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr; - +static AndroidBackendRegister *m_backendRegister = nullptr; static const char m_qtTag[] = "Qt"; static const char m_classErrorMsg[] = "Can't find class \"%s\""; static const char m_methodErrorMsg[] = "Can't find method \"%s%s\""; +Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0); + +Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface") +Q_DECLARE_JNI_CLASS(QtAccessibilityInterface, "org/qtproject/qt/android/QtAccessibilityInterface"); + namespace QtAndroid { QBasicMutex *platformInterfaceMutex() @@ -133,6 +103,7 @@ namespace QtAndroid void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration) { m_androidPlatformIntegration = androidPlatformIntegration; + QtAndroid::notifyNativePluginIntegrationReady((bool)m_androidPlatformIntegration); // flush the pending state if necessary. if (m_androidPlatformIntegration && (m_pendingApplicationState != -1)) { @@ -158,6 +129,21 @@ namespace QtAndroid : 0; } + QWindow *windowFromId(int windowId) + { + if (!qGuiApp) + return nullptr; + + for (QWindow *w : qGuiApp->allWindows()) { + if (!w->handle()) + continue; + QAndroidPlatformWindow *window = static_cast<QAndroidPlatformWindow *>(w->handle()); + if (window->nativeViewId() == windowId) + return w; + } + return nullptr; + } + int availableWidthPixels() { return m_availableWidthPixels; @@ -193,39 +179,79 @@ namespace QtAndroid return m_applicationClass; } - jobject activity() + // TODO move calls from here to where they logically belong + void setSystemUiVisibility(SystemUiVisibility uiVisibility) { - return m_activityObject; + AndroidBackendRegister *reg = QtAndroid::backendRegister(); + reg->callInterface<QtJniTypes::QtWindowInterface, void>("setSystemUiVisibility", + jint(uiVisibility)); } - jobject service() + bool isQtApplication() { - return m_serviceObject; + // 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 setSystemUiVisibility(SystemUiVisibility uiVisibility) + void initializeAccessibility() { - QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility)); + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "initializeAccessibility"); } - void notifyAccessibilityLocationChange() + void notifyAccessibilityLocationChange(uint accessibilityObjectId) { - QJniObject::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange"); + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "notifyLocationChange", accessibilityObjectId); } - void notifyObjectHide(uint accessibilityObjectId) + void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId) { - QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId); + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "notifyObjectHide", accessibilityObjectId, parentObjectId); + } + + void notifyObjectShow(uint parentObjectId) + { + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "notifyObjectShow", parentObjectId); } void notifyObjectFocus(uint accessibilityObjectId) { - QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId); + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "notifyObjectFocus", accessibilityObjectId); + } + + void notifyValueChanged(uint accessibilityObjectId, jstring value) + { + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "notifyValueChanged", accessibilityObjectId, value); } - void notifyQtAndroidPluginRunning(bool running) + void notifyScrolledEvent(uint accessibilityObjectId) { - QJniObject::callStaticMethod<void>(m_applicationClass, "notifyQtAndroidPluginRunning","(Z)V", running); + m_backendRegister->callInterface<QtJniTypes::QtAccessibilityInterface, void>( + "notifyScrolledEvent", accessibilityObjectId); + } + + void notifyNativePluginIntegrationReady(bool ready) + { + QJniObject::callStaticMethod<void>(m_applicationClass, + "notifyNativePluginIntegrationReady", + ready); } jobject createBitmap(QImage img, JNIEnv *env) @@ -234,7 +260,7 @@ namespace QtAndroid return 0; if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16) - img = img.convertToFormat(QImage::Format_RGBA8888); + img = std::move(img).convertToFormat(QImage::Format_RGBA8888); jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass, m_createBitmapMethodID, @@ -319,58 +345,7 @@ namespace QtAndroid QString manufacturer = QJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString(); QString model = QJniObject::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString(); - return manufacturer + QLatin1Char(' ') + model; - } - - int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth) - { - QJniEnvironment env; - if (!env.jniEnv()) - return -1; - - m_surfacesMutex.lock(); - int surfaceId = m_surfaceId++; - m_surfaces[surfaceId] = client; - m_surfacesMutex.unlock(); - - jint x = 0, y = 0, w = -1, h = -1; - if (!geometry.isNull()) { - x = geometry.x(); - y = geometry.y(); - w = std::max(geometry.width(), 1); - h = std::max(geometry.height(), 1); - } - env->CallStaticVoidMethod(m_applicationClass, - m_createSurfaceMethodID, - surfaceId, - jboolean(onTop), - x, y, w, h, - imageDepth); - return surfaceId; - } - - int insertNativeView(jobject view, const QRect &geometry) - { - m_surfacesMutex.lock(); - const int surfaceId = m_surfaceId++; - m_surfaces[surfaceId] = nullptr; // dummy - m_surfacesMutex.unlock(); - - jint x = 0, y = 0, w = -1, h = -1; - if (!geometry.isNull()) - geometry.getRect(&x, &y, &w, &h); - - QJniObject::callStaticMethod<void>(m_applicationClass, - "insertNativeView", - "(ILandroid/view/View;IIII)V", - surfaceId, - view, - x, - y, - qMax(w, 1), - qMax(h, 1)); - - return surfaceId; + return manufacturer + u' ' + model; } void setViewVisibility(jobject view, bool visible) @@ -382,69 +357,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"); @@ -456,31 +368,27 @@ namespace QtAndroid return m_assets; } + AndroidBackendRegister *backendRegister() + { + return m_backendRegister; + } + } // 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; - } - } + m_backendRegister = new AndroidBackendRegister(); - const char *nativeString = env->GetStringUTFChars(paramsString, 0); - QByteArray string = nativeString; - env->ReleaseStringUTFChars(paramsString, nativeString); + const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString()); - for (auto str : string.split('\t')) - m_applicationParams.append(str.split(' ')); + for (const QString &arg : argsList) + m_applicationParams.append(arg.toUtf8()); // Go home QDir::setCurrent(QDir::homePath()); @@ -520,7 +428,7 @@ static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/) Q_UNUSED(env); // The service must wait until the QCoreApplication starts otherwise onBind will be // called too early - if (m_serviceObject) + if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication()) QtAndroidPrivate::waitForServiceSetup(); } @@ -537,15 +445,22 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) vm->AttachCurrentThread(&env, &args); } + // Register type for invokeMethod() calls. + qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation"); + // Register resources if they are available if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists()) QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc")); - QVarLengthArray<const char *> params(m_applicationParams.size()); - for (int i = 0; i < m_applicationParams.size(); i++) - params[i] = static_cast<const char *>(m_applicationParams[i].constData()); + const int argc = m_applicationParams.size(); + QVarLengthArray<char *> argv(argc + 1); + for (int i = 0; i < argc; i++) + argv[i] = m_applicationParams[i].data(); + argv[argc] = nullptr; - int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data())); + startQtAndroidPluginCalled.fetchAndAddRelease(1); + const int ret = m_main(argc, argv.data()); + qInfo() << "main() returned" << ret; if (m_mainLibraryHnd) { int res = dlclose(m_mainLibraryHnd); @@ -553,10 +468,8 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) qWarning() << "dlclose failed:" << dlerror(); } - if (m_applicationClass) { - qWarning("exit app 0"); + if (m_applicationClass) QJniObject::callStaticMethod<void>(m_applicationClass, "quitApp", "()V"); - } sem_post(&m_terminateSemaphore); sem_wait(&m_exitSemaphore); @@ -592,17 +505,15 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/) QAndroidEventDispatcherStopper::instance()->goingToStop(false); } - sem_wait(&m_terminateSemaphore); + if (startQtAndroidPluginCalled.loadAcquire()) + sem_wait(&m_terminateSemaphore); + sem_destroy(&m_terminateSemaphore); env->DeleteGlobalRef(m_applicationClass); env->DeleteGlobalRef(m_classLoaderObject); if (m_resourcesObj) env->DeleteGlobalRef(m_resourcesObj); - if (m_activityObject) - env->DeleteGlobalRef(m_activityObject); - if (m_serviceObject) - env->DeleteGlobalRef(m_serviceObject); if (m_bitmapClass) env->DeleteGlobalRef(m_bitmapClass); if (m_ARGB_8888_BitmapConfigValue) @@ -613,54 +524,51 @@ 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; + delete m_backendRegister; + m_backendRegister = nullptr; sem_post(&m_exitSemaphore); } -static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h) +static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels, + jint screenHeightPixels, jint availableLeftPixels, + jint availableTopPixels, jint availableWidthPixels, + jint availableHeightPixels, jdouble xdpi, jdouble ydpi, + jdouble scaledDensity, jdouble density, jfloat refreshRate) { - QMutexLocker lock(&m_surfacesMutex); - const auto &it = m_surfaces.find(id); - if (it == m_surfaces.end()) - return; - - auto surfaceClient = it.value(); - if (surfaceClient) - surfaceClient->surfaceChanged(env, jSurface, w, h); -} + Q_UNUSED(availableLeftPixels) + Q_UNUSED(availableTopPixels) -static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, - jint screenWidthPixels, jint screenHeightPixels, - jint availableLeftPixels, jint availableTopPixels, - jint availableWidthPixels, jint availableHeightPixels, - jdouble xdpi, jdouble ydpi, - jdouble scaledDensity, jdouble density) -{ m_availableWidthPixels = availableWidthPixels; m_availableHeightPixels = availableHeightPixels; m_scaledDensity = scaledDensity; m_density = density; + const QSize screenSize(screenWidthPixels, screenHeightPixels); + // available geometry always starts from top left + const QRect availableGeometry(0, 0, availableWidthPixels, availableHeightPixels); + const QSize physicalSize(qRound(double(screenWidthPixels) / xdpi * 25.4), + qRound(double(screenHeightPixels) / ydpi * 25.4)); + QMutexLocker lock(&m_platformMutex); if (!m_androidPlatformIntegration) { - QAndroidPlatformIntegration::setDefaultDisplayMetrics(availableLeftPixels, - availableTopPixels, - availableWidthPixels, - availableHeightPixels, - qRound(double(screenWidthPixels) / xdpi * 25.4), - qRound(double(screenHeightPixels) / ydpi * 25.4), - screenWidthPixels, - screenHeightPixels); + QAndroidPlatformIntegration::setDefaultDisplayMetrics( + availableGeometry.left(), availableGeometry.top(), availableGeometry.width(), + availableGeometry.height(), physicalSize.width(), physicalSize.height(), + screenSize.width(), screenSize.height()); } else { - m_androidPlatformIntegration->setPhysicalSize(qRound(double(screenWidthPixels) / xdpi * 25.4), - qRound(double(screenHeightPixels) / ydpi * 25.4)); - m_androidPlatformIntegration->setScreenSize(screenWidthPixels, screenHeightPixels); - m_androidPlatformIntegration->setAvailableGeometry(QRect(availableLeftPixels, availableTopPixels, - availableWidthPixels, availableHeightPixels)); + m_androidPlatformIntegration->setScreenSizeParameters(physicalSize, screenSize, + availableGeometry); + m_androidPlatformIntegration->setRefreshRate(refreshRate); } } +Q_DECLARE_JNI_NATIVE_METHOD(setDisplayMetrics) static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) { @@ -680,10 +588,6 @@ static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size()))); } } - - QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen()); - if (screen->rasterSurfaces()) - QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); } static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state) @@ -753,11 +657,51 @@ static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint new QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native); QMutexLocker lock(&m_platformMutex); if (m_androidPlatformIntegration) { - QPlatformScreen *screen = m_androidPlatformIntegration->screen(); - QWindowSystemInterface::handleScreenOrientationChange(screen->screen(), - screenOrientation); + QAndroidPlatformScreen *screen = m_androidPlatformIntegration->screen(); + // Use invokeMethod to keep the certain order of the "geometry change" + // and "orientation change" event handling. + if (screen) { + QMetaObject::invokeMethod(screen, "setOrientation", Qt::AutoConnection, + Q_ARG(Qt::ScreenOrientation, screenOrientation)); + } } } +Q_DECLARE_JNI_NATIVE_METHOD(handleOrientationChanged) + +static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate) +{ + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->setRefreshRate(refreshRate); +} +Q_DECLARE_JNI_NATIVE_METHOD(handleRefreshRateChanged) + +static void handleScreenAdded(JNIEnv */*env*/, jclass /*cls*/, jint displayId) +{ + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->handleScreenAdded(displayId); +} +Q_DECLARE_JNI_NATIVE_METHOD(handleScreenAdded) + +static void handleScreenChanged(JNIEnv */*env*/, jclass /*cls*/, jint displayId) +{ + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->handleScreenChanged(displayId); +} +Q_DECLARE_JNI_NATIVE_METHOD(handleScreenChanged) + +static void handleScreenRemoved(JNIEnv */*env*/, jclass /*cls*/, jint displayId) +{ + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->handleScreenRemoved(displayId); +} +Q_DECLARE_JNI_NATIVE_METHOD(handleScreenRemoved) + +static void handleUiDarkModeChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newUiMode) +{ + QAndroidPlatformIntegration::updateColorScheme( + (newUiMode == 1 ) ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light); +} +Q_DECLARE_JNI_NATIVE_METHOD(handleUiDarkModeChanged) static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/, jint requestCode, @@ -778,117 +722,137 @@ static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent) } static JNINativeMethod methods[] = { - {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin}, - {"startQtApplication", "()V", (void *)startQtApplication}, - {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin}, - {"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication}, - {"terminateQt", "()V", (void *)terminateQt}, - {"waitForServiceSetup", "()V", (void *)waitForServiceSetup}, - {"setDisplayMetrics", "(IIIIIIDDDD)V", (void *)setDisplayMetrics}, - {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface}, - {"updateWindow", "()V", (void *)updateWindow}, - {"updateApplicationState", "(I)V", (void *)updateApplicationState}, - {"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged}, - {"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult}, - {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent}, - {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind} + { "startQtAndroidPlugin", "(Ljava/lang/String;)Z", (void *)startQtAndroidPlugin }, + { "startQtApplication", "()V", (void *)startQtApplication }, + { "quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin }, + { "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication }, + { "terminateQt", "()V", (void *)terminateQt }, + { "waitForServiceSetup", "()V", (void *)waitForServiceSetup }, + { "updateWindow", "()V", (void *)updateWindow }, + { "updateApplicationState", "(I)V", (void *)updateApplicationState }, + { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult }, + { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent }, + { "onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind } }; #define FIND_AND_CHECK_CLASS(CLASS_NAME) \ clazz = env->FindClass(CLASS_NAME); \ if (!clazz) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \ - return JNI_FALSE; \ + return false; \ } #define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ - return JNI_FALSE; \ + return false; \ } #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ - return JNI_FALSE; \ + return false; \ } #define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ - return JNI_FALSE; \ + return false; \ } #define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ - return JNI_FALSE; \ + return false; \ } -static int registerNatives(JNIEnv *env) +Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager") + +static bool registerNatives(QJniEnvironment &env) { jclass clazz; FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNative"); m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz)); - if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + if (!env.registerNativeMethods(m_applicationClass, + methods, sizeof(methods) / sizeof(methods[0]))) { __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); - return JNI_FALSE; + return false; } - GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V"); - GET_AND_CHECK_STATIC_METHOD(m_setSurfaceGeometryMethodID, m_applicationClass, "setSurfaceGeometry", "(IIIII)V"); - GET_AND_CHECK_STATIC_METHOD(m_destroySurfaceMethodID, m_applicationClass, "destroySurface", "(I)V"); + bool success = env.registerNativeMethods( + QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(), + { + Q_JNI_NATIVE_METHOD(setDisplayMetrics), + Q_JNI_NATIVE_METHOD(handleOrientationChanged), + Q_JNI_NATIVE_METHOD(handleRefreshRateChanged), + Q_JNI_NATIVE_METHOD(handleScreenAdded), + Q_JNI_NATIVE_METHOD(handleScreenChanged), + Q_JNI_NATIVE_METHOD(handleScreenRemoved), + Q_JNI_NATIVE_METHOD(handleUiDarkModeChanged) + }); + + if (!success) { + qCritical() << "QtDisplayManager: registerNativeMethods() failed"; + return JNI_FALSE; + } jmethodID methodID; GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); - jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); - GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;"); - jobject serviceObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + if (!contextObject) { + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;"); + contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + } + + if (!contextObject) { + __android_log_print(ANDROID_LOG_FATAL,"Qt", "Failed to get Activity or Service object"); + return false; + } + const auto releaseContextObject = qScopeGuard([&env, contextObject]{ + env->DeleteLocalRef(contextObject); + }); + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;"); m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID)); clazz = env->GetObjectClass(m_classLoaderObject); GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); - if (serviceObject) - m_serviceObject = env->NewGlobalRef(serviceObject); - - if (activityObject) - m_activityObject = env->NewGlobalRef(activityObject); - - jobject object = activityObject ? activityObject : serviceObject; - if (object) { - FIND_AND_CHECK_CLASS("android/content/ContextWrapper"); - GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;"); - m_assets = env->NewGlobalRef(env->CallObjectMethod(object, methodID)); - m_assetManager = AAssetManager_fromJava(env, m_assets); - - GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); - m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID)); - - FIND_AND_CHECK_CLASS("android/graphics/Bitmap"); - m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz)); - GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass - , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); - FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config"); - jfieldID fieldId; - GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;"); - m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); - GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;"); - m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); - - FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable"); - m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz)); - GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID, - m_bitmapDrawableClass, - "<init>", - "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V"); - } - - return JNI_TRUE; + + FIND_AND_CHECK_CLASS("android/content/ContextWrapper"); + GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;"); + m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID)); + m_assetManager = AAssetManager_fromJava(env.jniEnv(), m_assets); + + GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); + m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID)); + + FIND_AND_CHECK_CLASS("android/graphics/Bitmap"); + m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass, + "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); + FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config"); + jfieldID fieldId; + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;"); + m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); + GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;"); + m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); + + FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable"); + m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID, + m_bitmapDrawableClass, + "<init>", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V"); + + FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtActivityBase"); + m_qtActivityClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtServiceBase"); + m_qtServiceClass = static_cast<jclass>(env->NewGlobalRef(clazz)); + + return true; } QT_END_NAMESPACE @@ -901,36 +865,30 @@ 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) + || !AndroidBackendRegister::registerNatives()) { __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; } |