21#if QT_CONFIG(clipboard)
22#include "qandroidplatformclipboard.h"
24#if QT_CONFIG(draganddrop)
25#include "qandroidplatformdrag.h"
27#if QT_CONFIG(accessibility)
28#include "androidjniaccessibility.h"
33#include <android/api-level.h>
34#include <android/asset_manager_jni.h>
35#include <android/bitmap.h>
37#include <QtCore/private/qjnihelpers_p.h>
38#include <QtCore/private/qlocale_p.h>
39#include <QtCore/qbasicatomic.h>
40#include <QtCore/qjnienvironment.h>
41#include <QtCore/qjniobject.h>
42#include <QtCore/qprocess.h>
43#include <QtCore/qresource.h>
44#include <QtCore/qscopeguard.h>
45#include <QtCore/qthread.h>
46#include <QtCore/private/qandroiditemmodelproxy_p.h>
47#include <QtCore/private/qandroidmodelindexproxy_p.h>
48#include <QtGui/private/qguiapplication_p.h>
50#include <qpa/qwindowsysteminterface.h>
53using namespace Qt::StringLiterals;
57static jclass m_applicationClass =
nullptr;
76extern "C" typedef int (*
Main)(
int,
char **);
95Q_CONSTINIT
static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
97#if QT_CONFIG(accessibility)
98Q_DECLARE_JNI_CLASS(QtAccessibilityInterface,
"org/qtproject/qt/android/QtAccessibilityInterface");
107 return &m_platformMutex;
117 if (m_pendingApplicationState == Qt::ApplicationActive)
118 QtAndroidPrivate::handleResume();
119 else if (m_pendingApplicationState == Qt::ApplicationInactive)
120 QtAndroidPrivate::handlePause();
121 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(m_pendingApplicationState));
144 for (QWindow *w : qGuiApp->allWindows()) {
147 QAndroidPlatformWindow *window =
static_cast<QAndroidPlatformWindow *>(w->handle());
148 if (window->nativeViewId() == windowId)
161 return m_assetManager;
166 return m_applicationClass;
175 JNIEnv *env = QJniEnvironment::getJniEnv();
176 auto activity = QtAndroidPrivate::activity();
177 if (activity.isValid())
178 return env->IsInstanceOf(activity.object(), m_qtActivityClass);
179 auto service = QtAndroidPrivate::service();
180 if (service.isValid())
181 return env->IsInstanceOf(QtAndroidPrivate::service().object(), m_qtServiceClass);
187#if QT_CONFIG(accessibility)
248 QJniObject::callStaticMethod<
void>(m_applicationClass,
249 "notifyNativePluginIntegrationReady",
261 jobject config = m_ARGB_8888_BitmapConfigValue;
262 if (img.format() == QImage::Format_RGB16)
263 config = m_RGB_565_BitmapConfigValue;
264 else if (img.format() != QImage::Format_RGBA8888_Premultiplied)
265 img =
std::move(img).convertToFormat(QImage::Format_RGBA8888_Premultiplied);
267 jobject bitmap = env->CallStaticObjectMethod(
268 m_bitmapClass, m_createBitmapMethodID, img.width(), img.height(), config);
272 AndroidBitmapInfo info;
273 if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
274 env->DeleteLocalRef(bitmap);
279 if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
280 env->DeleteLocalRef(bitmap);
284 if (info.stride == uint(img.bytesPerLine())
285 && info.width == uint(img.width())
286 && info.height == uint(img.height())) {
287 memcpy(pixels, img.constBits(), info.stride * info.height);
289 uchar *bmpPtr =
static_cast<uchar *>(pixels);
290 const unsigned width = qMin(info.width, (uint)img.width());
291 const unsigned height = qMin(info.height, (uint)img.height());
292 for (
unsigned y = 0; y < height; y++, bmpPtr += info.stride)
293 memcpy(bmpPtr, img.constScanLine(y), width);
295 AndroidBitmap_unlockPixels(env, bitmap);
301 if (format != QImage::Format_RGBA8888
302 && format != QImage::Format_RGB16)
305 return env->CallStaticObjectMethod(m_bitmapClass,
306 m_createBitmapMethodID,
309 format == QImage::Format_RGB16
310 ? m_RGB_565_BitmapConfigValue
311 : m_ARGB_8888_BitmapConfigValue);
316 if (!bitmap || !m_bitmapDrawableClass || !m_resourcesObj)
319 return env->NewObject(m_bitmapDrawableClass,
320 m_bitmapDrawableConstructorMethodID,
347 QString manufacturer = QJniObject::getStaticObjectField(
"android/os/Build",
"MANUFACTURER",
"Ljava/lang/String;").toString();
348 QString model = QJniObject::getStaticObjectField(
"android/os/Build",
"MODEL",
"Ljava/lang/String;").toString();
350 return manufacturer + u' ' + model;
355 QJniObject::callStaticMethod<
void>(m_applicationClass,
357 "(Landroid/view/View;Z)V",
364 static bool block = qEnvironmentVariableIntValue(
"QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
393 QJniEnvironment qEnv;
394 if (!qEnv.isValid()) {
395 qCritical() <<
"Failed to initialize the JNI Environment";
399 if (!initJavaReferences(qEnv))
412 qCritical() <<
"Failed to init Qt application cleanup semaphores";
425 JNIEnv* env =
nullptr;
426 JavaVMAttachArgs args;
427 args.version = JNI_VERSION_1_6;
428 args.name =
"QtMainThread";
430 JavaVM *vm = QJniEnvironment::javaVM();
432 vm->AttachCurrentThread(&env, &args);
435 const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
436 const int argc = argsList.size();
437 QVarLengthArray<
char *> argv(argc + 1);
438 QList<QByteArray> argvData;
439 argvData.reserve(argc);
440 for (
int i = 0; i < argc; ++i) {
441 argvData.append(argsList.at(i).toUtf8());
442 argv[i] = argvData.back().data();
444 argv[argc] =
nullptr;
447 QDir::setCurrent(QDir::homePath());
450 void *mainLibraryHnd =
nullptr;
454 mainLibraryHnd = dlopen(argv.first(), 0);
455 if (Q_UNLIKELY(!mainLibraryHnd)) {
456 qCritical() <<
"dlopen failed:" << dlerror();
461 qWarning(
"No main library was specified; searching entire process (this is slow!)");
465 if (Q_UNLIKELY(!
m_main)) {
466 qCritical() <<
"dlsym failed:" << dlerror() << Qt::endl
467 <<
"Could not find main method";
472 qRegisterMetaType<Qt::ScreenOrientation>(
"Qt::ScreenOrientation");
475 if (QFile{QStringLiteral(
"assets:/android_rcc_bundle.rcc")}.exists())
476 QResource::registerResource(QStringLiteral(
"assets:/android_rcc_bundle.rcc"));
478 startQtAndroidPluginCalled.fetchAndAddRelease(1);
480 const int ret =
m_main(argc, argv.data());
481 qInfo() <<
"main() returned" << ret;
483 if (mainLibraryHnd) {
484 int res = dlclose(mainLibraryHnd);
486 qWarning() <<
"dlclose failed:" << dlerror();
489 QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() {
490 QtNative::callStaticMethod(
"setStarted",
false);
492 if (QtAndroid::isQtApplication()) {
494 auto activity = QtAndroidPrivate::activity();
495 if (activity.isValid())
496 activity.callMethod(
"finish");
497 auto service = QtAndroidPrivate::service();
498 if (service.isValid())
499 service.callMethod(
"stopSelf");
502 QtNative::callStaticMethod(
"terminateQtNativeApplication");
511 if (!qEnvironmentVariableIsSet(
"QT_ANDROID_NO_EXIT_CALL"))
517 if (m_applicationClass) {
518 env->DeleteGlobalRef(m_applicationClass);
519 m_applicationClass =
nullptr;
522 env->DeleteGlobalRef(m_resourcesObj);
526 env->DeleteGlobalRef(m_bitmapClass);
530 env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue);
534 env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
538 env->DeleteGlobalRef(m_bitmapDrawableClass);
542 env->DeleteGlobalRef(m_assets);
546 env->DeleteGlobalRef(m_qtActivityClass);
550 env->DeleteGlobalRef(m_qtServiceClass);
560 if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication())
561 QtAndroidPrivate::waitForServiceSetup();
568 if (QAndroidEventDispatcherStopper::instance()->stopped()
569 || QtAndroidPrivate::service().isValid()) {
570 QAndroidEventDispatcherStopper::instance()->startAll();
571 QCoreApplication::quit();
572 QAndroidEventDispatcherStopper::instance()->goingToStop(
false);
575 if (startQtAndroidPluginCalled.loadAcquire())
576 sem_wait(&m_stopQtSemaphore);
578 sem_destroy(&m_stopQtSemaphore);
580 clearJavaReferences(env);
582 m_androidPlatformIntegration =
nullptr;
583 delete m_androidAssetsFileEngineHandler;
584 m_androidAssetsFileEngineHandler =
nullptr;
585 delete m_androidContentFileEngineHandler;
586 m_androidContentFileEngineHandler =
nullptr;
587 delete m_androidApkFileEngineHandler;
588 m_androidApkFileEngineHandler =
nullptr;
589 delete m_backendRegister;
590 m_backendRegister =
nullptr;
591 sem_post(&m_exitSemaphore);
594 QtNative::callStaticMethod<QtThread>(
"getQtThread").callMethod(
"exit");
598 jint availableWidth, jint availableHeight)
600 QMutexLocker lock(&m_platformMutex);
602 const QRect availableGeometry(0, 0, availableWidth, availableHeight);
603 if (m_androidPlatformIntegration)
604 m_androidPlatformIntegration->setAvailableGeometry(availableGeometry);
605 else if (QAndroidPlatformScreen::defaultAvailableGeometry().isNull())
606 QAndroidPlatformScreen::defaultAvailableGeometry() = availableGeometry;
608Q_DECLARE_JNI_NATIVE_METHOD(handleLayoutSizeChanged)
612 QMutexLocker lock(&m_platformMutex);
621 if (state == Qt::ApplicationActive)
622 QtAndroidPrivate::handleResume();
623 else if (state == Qt::ApplicationInactive)
624 QtAndroidPrivate::handlePause();
629 if (state <= Qt::ApplicationInactive) {
639 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
640 if (state == Qt::ApplicationSuspended)
644 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
651#ifndef QT_NO_SYSTEMLOCALE
653 { QSystemLocale dummy; }
655 QCoreApplication::postEvent(QCoreApplication::instance(),
new QEvent(QEvent::LocaleChange));
656 QCoreApplication::postEvent(QCoreApplication::instance(),
new QEvent(QEvent::LanguageChange));
663 static const Qt::ScreenOrientation orientations[] = {
664 Qt::PortraitOrientation,
665 Qt::LandscapeOrientation,
666 Qt::InvertedPortraitOrientation,
667 Qt::InvertedLandscapeOrientation
681 Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4];
682 Qt::ScreenOrientation native = orientations[nativeOrientation - 1];
684 QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
685 QMutexLocker lock(&m_platformMutex);
691 QMetaObject::invokeMethod(screen,
"setOrientation", Qt::AutoConnection,
692 Q_ARG(Qt::ScreenOrientation, screenOrientation));
696Q_DECLARE_JNI_NATIVE_METHOD(handleOrientationChanged)
700 if (m_androidPlatformIntegration)
701 m_androidPlatformIntegration->setRefreshRate(refreshRate);
703Q_DECLARE_JNI_NATIVE_METHOD(handleRefreshRateChanged)
707 if (m_androidPlatformIntegration)
708 m_androidPlatformIntegration->handleScreenAdded(displayId);
710Q_DECLARE_JNI_NATIVE_METHOD(handleScreenAdded)
714 if (m_androidPlatformIntegration)
715 m_androidPlatformIntegration->handleScreenChanged(displayId);
717Q_DECLARE_JNI_NATIVE_METHOD(handleScreenChanged)
721 if (m_androidPlatformIntegration)
722 m_androidPlatformIntegration->handleScreenRemoved(displayId);
724Q_DECLARE_JNI_NATIVE_METHOD(handleScreenRemoved)
728 QAndroidPlatformIntegration::updateColorScheme(
729 (newUiMode == 1 ) ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light);
731Q_DECLARE_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
737Q_DECLARE_JNI_NATIVE_METHOD(handleScreenDensityChanged)
744 QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data);
749 QtAndroidPrivate::handleNewIntent(env, data);
754 return QtAndroidPrivate::callOnBindListener(intent);
758 {
"initAndroidQpaPlugin",
"()Z", (
void *)initAndroidQpaPlugin },
759 {
"startQtNativeApplication",
"(Ljava/lang/String;)V", (
void *)startQtNativeApplication },
760 {
"terminateQtNativeApplication",
"()V", (
void *)terminateQtNativeApplication },
761 {
"waitForServiceSetup",
"()V", (
void *)waitForServiceSetup },
762 {
"updateApplicationState",
"(I)V", (
void *)updateApplicationState },
763 {
"onActivityResult",
"(IILandroid/content/Intent;)V", (
void *)onActivityResult },
764 {
"onNewIntent",
"(Landroid/content/Intent;)V", (
void *)onNewIntent },
765 {
"onBind",
"(Landroid/content/Intent;)Landroid/os/IBinder;", (
void *)onBind },
766 {
"updateLocale",
"()V", (
void *)updateLocale },
769#define FIND_AND_CHECK_CLASS(CLASS_NAME) clazz
770 = env->FindClass(CLASS_NAME); if
772 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME);
774}
776#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) VAR
777 = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); if
779 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
781}
783#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) VAR
784 = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); if
786 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
788}
790#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) VAR
791 = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); if
793 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE);
795}
797#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) VAR
798 = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); if
800 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE);
802}
808 bool success = env.registerNativeMethods(m_applicationClass,
810 success &= env.registerNativeMethods(
811 QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(),
813 Q_JNI_NATIVE_METHOD(handleLayoutSizeChanged),
814 Q_JNI_NATIVE_METHOD(handleOrientationChanged),
815 Q_JNI_NATIVE_METHOD(handleRefreshRateChanged),
816 Q_JNI_NATIVE_METHOD(handleScreenAdded),
817 Q_JNI_NATIVE_METHOD(handleScreenChanged),
818 Q_JNI_NATIVE_METHOD(handleScreenRemoved),
819 Q_JNI_NATIVE_METHOD(handleUiDarkModeChanged),
820 Q_JNI_NATIVE_METHOD(handleScreenDensityChanged)
824 && QtAndroidInput::registerNatives(env)
825 && QtAndroidMenu::registerNatives(env)
826#if QT_CONFIG(accessibility)
827 && QtAndroidAccessibility::registerNatives(env)
829 && QtAndroidDialogHelpers::registerNatives(env)
830#if QT_CONFIG(clipboard)
831 && QAndroidPlatformClipboard::registerNatives(env)
833#if QT_CONFIG(draganddrop)
834 && QAndroidPlatformDrag::registerNatives(env)
836 && QAndroidPlatformWindow::registerNatives(env)
837 && QtAndroidWindowEmbedding::registerNatives(env)
838 && AndroidBackendRegister::registerNatives()
839 && QAndroidModelIndexProxy::registerNatives(env)
840 && QAndroidItemModelProxy::registerAbstractNatives(env)
841 && QAndroidItemModelProxy::registerProxyNatives(env);
848 if (m_applicationClass)
853 m_applicationClass =
static_cast<jclass>(env->NewGlobalRef(clazz));
858 jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
859 if (!contextObject) {
861 contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
864 if (!contextObject) {
865 __android_log_print(ANDROID_LOG_FATAL,
"Qt",
"Failed to get Activity or Service object");
868 const auto releaseContextObject = qScopeGuard([&env, contextObject]{
869 env->DeleteLocalRef(contextObject);
874 m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
878 m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
881 m_bitmapClass =
static_cast<jclass>(env->NewGlobalRef(clazz));
883 "createBitmap",
"(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
887 m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
889 m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
892 m_bitmapDrawableClass =
static_cast<jclass>(env->NewGlobalRef(clazz));
894 m_bitmapDrawableClass,
895 "<init>",
"(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
898 m_qtActivityClass =
static_cast<jclass>(env->NewGlobalRef(clazz));
900 m_qtServiceClass =
static_cast<jclass>(env->NewGlobalRef(clazz));
903 QThread::currentThread()->setObjectName(
"QtMainLoopThread");
905 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(
false);
912extern "C" Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *,
void *)
914 static bool initialized =
false;
916 return JNI_VERSION_1_6;
922 if (!env.isValid()) {
923 __android_log_print(ANDROID_LOG_FATAL,
"Qt",
"Failed to initialize the JNI Environment");
927 if (!initJavaReferences(env))
930 if (!registerNatives(env)) {
931 __android_log_print(ANDROID_LOG_FATAL,
"Qt",
"registerNatives failed");
935 __android_log_print(ANDROID_LOG_INFO,
"Qt",
"Qt platform plugin started");
936 return JNI_VERSION_1_6;
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
static bool initJavaReferences(QJniEnvironment &env)
static const char m_qtTag[]
static sem_t m_exitSemaphore
#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE)
static jclass m_qtActivityClass
static AndroidAssetsFileEngineHandler * m_androidAssetsFileEngineHandler
static jclass m_bitmapClass
static void terminateQtNativeApplication(JNIEnv *env, jclass)
static jclass m_bitmapDrawableClass
static void waitForServiceSetup(JNIEnv *env, jclass)
static const char m_staticFieldErrorMsg[]
static void handleLayoutSizeChanged(JNIEnv *, jclass, jint availableWidth, jint availableHeight)
static jobject m_resourcesObj
static jmethodID m_bitmapDrawableConstructorMethodID
static void handleScreenChanged(JNIEnv *, jclass, jint displayId)
static QAndroidPlatformIntegration * m_androidPlatformIntegration
static AndroidContentFileEngineHandler * m_androidContentFileEngineHandler
static bool registerNatives(QJniEnvironment &env)
static JNINativeMethod methods[]
static AndroidBackendRegister * m_backendRegister
static void onNewIntent(JNIEnv *env, jclass, jobject data)
#define FIND_AND_CHECK_CLASS(CLASS_NAME)
#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
static void handleUiDarkModeChanged(JNIEnv *, jobject, jint newUiMode)
static int m_pendingApplicationState
static void handleRefreshRateChanged(JNIEnv *, jclass, jfloat refreshRate)
static QBasicMutex m_platformMutex
static void updateApplicationState(JNIEnv *, jobject, jint state)
static void clearJavaReferences(JNIEnv *env)
int(* Main)(int, char **)
static void handleOrientationChanged(JNIEnv *, jobject, jint newRotation, jint nativeOrientation)
static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString)
static const char m_methodErrorMsg[]
static bool initAndroidQpaPlugin(JNIEnv *jenv, jobject object)
static void handleScreenRemoved(JNIEnv *, jclass, jint displayId)
static void updateLocale(JNIEnv *, jobject)
static jobject m_RGB_565_BitmapConfigValue
static const char m_classErrorMsg[]
static jobject m_ARGB_8888_BitmapConfigValue
static sem_t m_stopQtSemaphore
static jclass m_qtServiceClass
static AAssetManager * m_assetManager
static jmethodID m_createBitmapMethodID
static jobject onBind(JNIEnv *, jclass, jobject intent)
static void onActivityResult(JNIEnv *, jclass, jint requestCode, jint resultCode, jobject data)
static void handleScreenDensityChanged(JNIEnv *, jclass, jdouble density)
static QAndroidApkFileEngineHandler * m_androidApkFileEngineHandler
static void handleScreenAdded(JNIEnv *, jclass, jint displayId)
static QAndroidEventDispatcherStopper * instance()
void goingToStop(bool stop)
\inmodule QtCore\reentrant
const char * classErrorMsgFmt()
void setViewVisibility(jobject view, bool visible)
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
QBasicMutex * platformInterfaceMutex()
jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env=nullptr)
QWindow * topLevelWindowAt(const QPoint &globalPos)
QAndroidPlatformIntegration * androidPlatformIntegration()
jobject createBitmap(QImage img, JNIEnv *env=nullptr)
AndroidBackendRegister * backendRegister()
bool blockEventLoopsWhenSuspended()
QWindow * windowFromId(int windowId)
jclass applicationClass()
void notifyNativePluginIntegrationReady(bool ready)
AAssetManager * assetManager()
const char * staticFieldErrorMsgFmt()
const char * methodErrorMsgFmt()
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")