Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
androidjnimain.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <dlfcn.h>
6#include <pthread.h>
7#include <qplugin.h>
8#include <semaphore.h>
9
13#include "androidjnimain.h"
14#include "androidjnimenu.h"
20#if QT_CONFIG(clipboard)
21#include "qandroidplatformclipboard.h"
22#endif
23#if QT_CONFIG(accessibility)
24#include "androidjniaccessibility.h"
25#endif
28
29#include <android/api-level.h>
30#include <android/asset_manager_jni.h>
31#include <android/bitmap.h>
32
33#include <QtCore/private/qjnihelpers_p.h>
34#include <QtCore/qbasicatomic.h>
35#include <QtCore/qjnienvironment.h>
36#include <QtCore/qjniobject.h>
37#include <QtCore/qprocess.h>
38#include <QtCore/qresource.h>
39#include <QtCore/qscopeguard.h>
40#include <QtCore/qthread.h>
41#include <QtCore/private/qandroiditemmodelproxy_p.h>
42#include <QtCore/private/qandroidmodelindexproxy_p.h>
43#include <QtGui/private/qguiapplication_p.h>
44#include <QtGui/private/qhighdpiscaling_p.h>
45
46#include <qpa/qwindowsysteminterface.h>
47
48
49using namespace Qt::StringLiterals;
50
51QT_BEGIN_NAMESPACE
52
53static jclass m_applicationClass = nullptr;
54static AAssetManager *m_assetManager = nullptr;
55static jobject m_assets = nullptr;
56static jobject m_resourcesObj = nullptr;
57
58static jclass m_qtActivityClass = nullptr;
59static jclass m_qtServiceClass = nullptr;
60
62static QBasicMutex m_platformMutex;
63
64static jclass m_bitmapClass = nullptr;
68
71
72extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
73static Main m_main = nullptr;
75
77
78static double m_density = 1.0;
79
83
85
86static const char m_qtTag[] = "Qt";
87static const char m_classErrorMsg[] = "Can't find class \"%s\"";
88static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
89
90Q_CONSTINIT static QBasicAtomicInt startQtAndroidPluginCalled = Q_BASIC_ATOMIC_INITIALIZER(0);
91
92#if QT_CONFIG(accessibility)
93Q_DECLARE_JNI_CLASS(QtAccessibilityInterface, "org/qtproject/qt/android/QtAccessibilityInterface");
94#endif
95
96Q_DECLARE_JNI_CLASS(QtThread, "org/qtproject/qt/android/QtThread");
97
98namespace QtAndroid
99{
101 {
102 return &m_platformMutex;
103 }
104
106 {
107 m_androidPlatformIntegration = androidPlatformIntegration;
109
110 // flush the pending state if necessary.
112 if (m_pendingApplicationState == Qt::ApplicationActive)
113 QtAndroidPrivate::handleResume();
114 else if (m_pendingApplicationState == Qt::ApplicationInactive)
115 QtAndroidPrivate::handlePause();
116 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(m_pendingApplicationState));
117 }
118
120 }
121
126
127 QWindow *topLevelWindowAt(const QPoint &globalPos)
128 {
131 : 0;
132 }
133
134 QWindow *windowFromId(int windowId)
135 {
136 if (!qGuiApp)
137 return nullptr;
138
139 for (QWindow *w : qGuiApp->allWindows()) {
140 if (!w->handle())
141 continue;
142 QAndroidPlatformWindow *window = static_cast<QAndroidPlatformWindow *>(w->handle());
143 if (window->nativeViewId() == windowId)
144 return w;
145 }
146 return nullptr;
147 }
148
150 {
151 return m_density;
152 }
153
155 {
156 return m_assetManager;
157 }
158
160 {
161 return m_applicationClass;
162 }
163
165 {
166 // Returns true if the app is a Qt app, i.e. Qt controls the whole app and
167 // the Activity/Service is created by Qt. Returns false if instead Qt is
168 // embedded into a native Android app, where the Activity/Service is created
169 // by the user, outside of Qt, and Qt content is added as a view.
170 JNIEnv *env = QJniEnvironment::getJniEnv();
171 auto activity = QtAndroidPrivate::activity();
172 if (activity.isValid())
173 return env->IsInstanceOf(activity.object(), m_qtActivityClass);
174 auto service = QtAndroidPrivate::service();
175 if (service.isValid())
176 return env->IsInstanceOf(QtAndroidPrivate::service().object(), m_qtServiceClass);
177 // return true as default as Qt application is our default use case.
178 // famous last words: we should not end up here
179 return true;
180 }
181
182#if QT_CONFIG(accessibility)
184 {
186 "notifyLocationChange", accessibilityObjectId);
187 }
188
190 {
192 "notifyObjectHide", accessibilityObjectId, parentObjectId);
193 }
194
196 {
198 "notifyObjectShow", parentObjectId);
199 }
200
202 {
204 "notifyObjectFocus", accessibilityObjectId);
205 }
206
208 {
210 "notifyValueChanged", accessibilityObjectId, value);
211 }
212
214 {
216 "notifyDescriptionOrNameChanged", accessibilityObjectId, value);
217 }
218
220 {
222 "notifyScrolledEvent", accessibilityObjectId);
223 }
224
226 {
228 "notifyAnnouncementEvent", accessibilityObjectId, message);
229 }
230#endif //QT_CONFIG(accessibility)
231
233 {
234 QJniObject::callStaticMethod<void>(m_applicationClass,
235 "notifyNativePluginIntegrationReady",
236 ready);
237 }
238
239 jobject createBitmap(QImage img, JNIEnv *env)
240 {
241 if (!m_bitmapClass)
242 return 0;
243
244 if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16)
245 img = std::move(img).convertToFormat(QImage::Format_RGBA8888);
246
247 jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass,
248 m_createBitmapMethodID,
249 img.width(),
250 img.height(),
251 img.format() == QImage::Format_RGBA8888
252 ? m_ARGB_8888_BitmapConfigValue
253 : m_RGB_565_BitmapConfigValue);
254 if (!bitmap)
255 return 0;
256
257 AndroidBitmapInfo info;
258 if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
259 env->DeleteLocalRef(bitmap);
260 return 0;
261 }
262
263 void *pixels;
264 if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
265 env->DeleteLocalRef(bitmap);
266 return 0;
267 }
268
269 if (info.stride == uint(img.bytesPerLine())
270 && info.width == uint(img.width())
271 && info.height == uint(img.height())) {
272 memcpy(pixels, img.constBits(), info.stride * info.height);
273 } else {
274 uchar *bmpPtr = static_cast<uchar *>(pixels);
275 const unsigned width = qMin(info.width, (uint)img.width()); //should be the same
276 const unsigned height = qMin(info.height, (uint)img.height()); //should be the same
277 for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
278 memcpy(bmpPtr, img.constScanLine(y), width);
279 }
280 AndroidBitmap_unlockPixels(env, bitmap);
281 return bitmap;
282 }
283
284 jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
285 {
286 if (format != QImage::Format_RGBA8888
287 && format != QImage::Format_RGB16)
288 return 0;
289
290 return env->CallStaticObjectMethod(m_bitmapClass,
291 m_createBitmapMethodID,
292 width,
293 height,
294 format == QImage::Format_RGB16
295 ? m_RGB_565_BitmapConfigValue
296 : m_ARGB_8888_BitmapConfigValue);
297 }
298
299 jobject createBitmapDrawable(jobject bitmap, JNIEnv *env)
300 {
301 if (!bitmap || !m_bitmapDrawableClass || !m_resourcesObj)
302 return 0;
303
304 return env->NewObject(m_bitmapDrawableClass,
305 m_bitmapDrawableConstructorMethodID,
306 m_resourcesObj,
307 bitmap);
308 }
309
310 const char *classErrorMsgFmt()
311 {
312 return m_classErrorMsg;
313 }
314
315 const char *methodErrorMsgFmt()
316 {
317 return m_methodErrorMsg;
318 }
319
320 const char *qtTagText()
321 {
322 return m_qtTag;
323 }
324
325 QString deviceName()
326 {
327 QString manufacturer = QJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString();
328 QString model = QJniObject::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString();
329
330 return manufacturer + u' ' + model;
331 }
332
333 void setViewVisibility(jobject view, bool visible)
334 {
335 QJniObject::callStaticMethod<void>(m_applicationClass,
336 "setViewVisibility",
337 "(Landroid/view/View;Z)V",
338 view,
339 visible);
340 }
341
343 {
344 static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
345 return block;
346 }
347
349 {
350 return m_assets;
351 }
352
357
358} // namespace QtAndroid
359
360static bool initJavaReferences(QJniEnvironment &env);
361
362static bool initAndroidQpaPlugin(JNIEnv *jenv, jobject object)
363{
364 Q_UNUSED(jenv)
365 Q_UNUSED(object)
366
367 // Init all the Java refs, if they haven't already been initialized. They get initialized
368 // when the library is loaded, but in case Qt is terminated, they are cleared, and in case
369 // Qt is then started again JNI_OnLoad will not be called again, since the library is already
370 // loaded - in that case we need to init again here, hence the check.
371 // TODO QTBUG-130614 QtCore also inits some Java references in qjnihelpers - we probably
372 // want to reset those, too.
373 QJniEnvironment qEnv;
374 if (!qEnv.isValid()) {
375 qCritical() << "Failed to initialize the JNI Environment";
376 return false;
377 }
378
379 if (!initJavaReferences(qEnv))
380 return false;
381
383
384 // File engine handler instantiation registers the handler
388
390
391 if (sem_init(&m_exitSemaphore, 0, 0) == -1 && sem_init(&m_stopQtSemaphore, 0, 0) == -1) {
392 qCritical() << "Failed to init Qt application cleanup semaphores";
393 return false;
394 }
395
396 return true;
397}
398
399static void startQtNativeApplication(JNIEnv *jenv, jobject object, jstring paramsString)
400{
401 Q_UNUSED(jenv)
402 Q_UNUSED(object)
403
404 {
405 JNIEnv* env = nullptr;
406 JavaVMAttachArgs args;
407 args.version = JNI_VERSION_1_6;
408 args.name = "QtMainThread";
409 args.group = NULL;
410 JavaVM *vm = QJniEnvironment::javaVM();
411 if (vm)
412 vm->AttachCurrentThread(&env, &args);
413 }
414
415 const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
416 const int argc = argsList.size();
417 QVarLengthArray<char *> argv(argc + 1);
418 QList<QByteArray> argvData;
419 argvData.reserve(argc);
420 for (int i = 0; i < argc; ++i) {
421 argvData.append(argsList.at(i).toUtf8());
422 argv[i] = argvData.back().data();
423 }
424 argv[argc] = nullptr;
425
426 // Go home
427 QDir::setCurrent(QDir::homePath());
428
429 // look for main()
430 void *mainLibraryHnd = nullptr;
431 if (argc) {
432 // Obtain a handle to the main library (the library that contains the main() function).
433 // This library should already be loaded, and calling dlopen() will just return a reference to it.
434 mainLibraryHnd = dlopen(argv.first(), 0);
435 if (Q_UNLIKELY(!mainLibraryHnd)) {
436 qCritical() << "dlopen failed:" << dlerror();
437 return;
438 }
439 m_main = (Main)dlsym(mainLibraryHnd, "main");
440 } else {
441 qWarning("No main library was specified; searching entire process (this is slow!)");
442 m_main = (Main)dlsym(RTLD_DEFAULT, "main");
443 }
444
445 if (Q_UNLIKELY(!m_main)) {
446 qCritical() << "dlsym failed:" << dlerror() << Qt::endl
447 << "Could not find main method";
448 return;
449 }
450
451 // Register type for invokeMethod() calls.
452 qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
453
454 // Register resources if they are available
455 if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
456 QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
457
458 startQtAndroidPluginCalled.fetchAndAddRelease(1);
459
460 const int ret = m_main(argc, argv.data());
461 qInfo() << "main() returned" << ret;
462
463 if (mainLibraryHnd) {
464 int res = dlclose(mainLibraryHnd);
465 if (res < 0)
466 qWarning() << "dlclose failed:" << dlerror();
467 }
468
469 QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() {
470 QtNative::callStaticMethod("setStarted", false);
471
472 if (QtAndroid::isQtApplication()) {
473 // Now, that the Qt application has exited, tear down the Activity and Service
474 auto activity = QtAndroidPrivate::activity();
475 if (activity.isValid())
476 activity.callMethod("finish");
477 auto service = QtAndroidPrivate::service();
478 if (service.isValid())
479 service.callMethod("stopSelf");
480 } else {
481 // For the embedded case, we only need to terminate Qt
482 QtNative::callStaticMethod("terminateQtNativeApplication");
483 }
484 });
485
486 sem_post(&m_stopQtSemaphore);
487 sem_wait(&m_exitSemaphore);
488 sem_destroy(&m_exitSemaphore);
489
490 // We must call exit() to ensure that all global objects will be destructed
491 if (!qEnvironmentVariableIsSet("QT_ANDROID_NO_EXIT_CALL"))
492 exit(ret);
493}
494
495static void clearJavaReferences(JNIEnv *env)
496{
497 if (m_applicationClass) {
498 env->DeleteGlobalRef(m_applicationClass);
499 m_applicationClass = nullptr;
500 }
501 if (m_resourcesObj) {
502 env->DeleteGlobalRef(m_resourcesObj);
503 m_resourcesObj = nullptr;
504 }
505 if (m_bitmapClass) {
506 env->DeleteGlobalRef(m_bitmapClass);
507 m_bitmapClass = nullptr;
508 }
510 env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue);
512 }
514 env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
516 }
518 env->DeleteGlobalRef(m_bitmapDrawableClass);
519 m_bitmapDrawableClass = nullptr;
520 }
521 if (m_assets) {
522 env->DeleteGlobalRef(m_assets);
523 m_assets = nullptr;
524 }
525 if (m_qtActivityClass) {
526 env->DeleteGlobalRef(m_qtActivityClass);
527 m_qtActivityClass = nullptr;
528 }
529 if (m_qtServiceClass) {
530 env->DeleteGlobalRef(m_qtServiceClass);
531 m_qtServiceClass = nullptr;
532 }
533}
534
535static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
536{
537 Q_UNUSED(env);
538 // The service must wait until the QCoreApplication starts otherwise onBind will be
539 // called too early
540 if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication())
541 QtAndroidPrivate::waitForServiceSetup();
542}
543
544static void terminateQtNativeApplication(JNIEnv *env, jclass /*clazz*/)
545{
546 // QAndroidEventDispatcherStopper is stopped when the user uses the task manager
547 // to kill the application. Also, in case of a service ensure to call quit().
548 if (QAndroidEventDispatcherStopper::instance()->stopped()
549 || QtAndroidPrivate::service().isValid()) {
550 QAndroidEventDispatcherStopper::instance()->startAll();
551 QCoreApplication::quit();
552 QAndroidEventDispatcherStopper::instance()->goingToStop(false);
553 }
554
555 if (startQtAndroidPluginCalled.loadAcquire())
556 sem_wait(&m_stopQtSemaphore);
557
558 sem_destroy(&m_stopQtSemaphore);
559
560 clearJavaReferences(env);
561
562 m_androidPlatformIntegration = nullptr;
563 delete m_androidAssetsFileEngineHandler;
564 m_androidAssetsFileEngineHandler = nullptr;
565 delete m_androidContentFileEngineHandler;
566 m_androidContentFileEngineHandler = nullptr;
567 delete m_androidApkFileEngineHandler;
568 m_androidApkFileEngineHandler = nullptr;
569 delete m_backendRegister;
570 m_backendRegister = nullptr;
571 sem_post(&m_exitSemaphore);
572
573 // Terminate the QtThread
574 QtNative::callStaticMethod<QtThread>("getQtThread").callMethod("exit");
575}
576
577static void handleLayoutSizeChanged(JNIEnv * /*env*/, jclass /*clazz*/,
578 jint availableWidth, jint availableHeight)
579{
580 QMutexLocker lock(&m_platformMutex);
581 // available geometry always starts from top left
582 const QRect availableGeometry(0, 0, availableWidth, availableHeight);
583 if (m_androidPlatformIntegration)
584 m_androidPlatformIntegration->setAvailableGeometry(availableGeometry);
585 else if (QAndroidPlatformScreen::defaultAvailableGeometry().isNull())
586 QAndroidPlatformScreen::defaultAvailableGeometry() = availableGeometry;
587}
588Q_DECLARE_JNI_NATIVE_METHOD(handleLayoutSizeChanged)
589
590static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
591{
592 QMutexLocker lock(&m_platformMutex);
595 return;
596 }
597
598 // We're about to call user code from the Android thread, since we don't know
599 //the side effects we'll unlock first!
600 lock.unlock();
601 if (state == Qt::ApplicationActive)
602 QtAndroidPrivate::handleResume();
603 else if (state == Qt::ApplicationInactive)
604 QtAndroidPrivate::handlePause();
605 lock.relock();
607 return;
608
609 if (state <= Qt::ApplicationInactive) {
610 // NOTE: sometimes we will receive two consecutive suspended notifications,
611 // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents()
612 // will deadlock since the dispatcher has been stopped in the first suspended notification.
613 // To avoid the deadlock we simply return if we found the event dispatcher has been stopped.
615 return;
616
617 // Don't send timers and sockets events anymore if we are going to hide all windows
619 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
620 if (state == Qt::ApplicationSuspended)
622 } else {
624 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
626 }
627}
628
629static void updateLocale(JNIEnv */*env*/, jobject /*thiz*/)
630{
631 QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::LocaleChange));
632 QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::LanguageChange));
633}
634
635static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation)
636{
637 // Array of orientations rotated in 90 degree increments, counterclockwise
638 // (same direction as Android measures angles)
639 static const Qt::ScreenOrientation orientations[] = {
640 Qt::PortraitOrientation,
641 Qt::LandscapeOrientation,
642 Qt::InvertedPortraitOrientation,
643 Qt::InvertedLandscapeOrientation
644 };
645
646 // The Android API defines the following constants:
647 // ROTATION_0 : 0
648 // ROTATION_90 : 1
649 // ROTATION_180 : 2
650 // ROTATION_270 : 3
651 // ORIENTATION_PORTRAIT : 1
652 // ORIENTATION_LANDSCAPE : 2
653
654 // and newRotation is how much the current orientation is rotated relative to nativeOrientation
655
656 // which means that we can be really clever here :)
657 Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4];
658 Qt::ScreenOrientation native = orientations[nativeOrientation - 1];
659
660 QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
661 QMutexLocker lock(&m_platformMutex);
664 // Use invokeMethod to keep the certain order of the "geometry change"
665 // and "orientation change" event handling.
666 if (screen) {
667 QMetaObject::invokeMethod(screen, "setOrientation", Qt::AutoConnection,
668 Q_ARG(Qt::ScreenOrientation, screenOrientation));
669 }
670 }
671}
672Q_DECLARE_JNI_NATIVE_METHOD(handleOrientationChanged)
673
674static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate)
675{
676 if (m_androidPlatformIntegration)
677 m_androidPlatformIntegration->setRefreshRate(refreshRate);
678}
679Q_DECLARE_JNI_NATIVE_METHOD(handleRefreshRateChanged)
680
681static void handleScreenAdded(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
682{
683 if (m_androidPlatformIntegration)
684 m_androidPlatformIntegration->handleScreenAdded(displayId);
685}
686Q_DECLARE_JNI_NATIVE_METHOD(handleScreenAdded)
687
688static void handleScreenChanged(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
689{
690 if (m_androidPlatformIntegration)
691 m_androidPlatformIntegration->handleScreenChanged(displayId);
692}
693Q_DECLARE_JNI_NATIVE_METHOD(handleScreenChanged)
694
695static void handleScreenRemoved(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
696{
697 if (m_androidPlatformIntegration)
698 m_androidPlatformIntegration->handleScreenRemoved(displayId);
699}
700Q_DECLARE_JNI_NATIVE_METHOD(handleScreenRemoved)
701
702static void handleUiDarkModeChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newUiMode)
703{
704 QAndroidPlatformIntegration::updateColorScheme(
705 (newUiMode == 1 ) ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light);
706}
707Q_DECLARE_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
708
709static void handleScreenDensityChanged(JNIEnv */*env*/, jclass /*cls*/, jdouble density)
710{
711 m_density = density;
712}
713Q_DECLARE_JNI_NATIVE_METHOD(handleScreenDensityChanged)
714
715static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
716 jint requestCode,
717 jint resultCode,
718 jobject data)
719{
720 QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data);
721}
722
723static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data)
724{
725 QtAndroidPrivate::handleNewIntent(env, data);
726}
727
728static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
729{
730 return QtAndroidPrivate::callOnBindListener(intent);
731}
732
734 { "initAndroidQpaPlugin", "()Z", (void *)initAndroidQpaPlugin },
735 { "startQtNativeApplication", "(Ljava/lang/String;)V", (void *)startQtNativeApplication },
736 { "terminateQtNativeApplication", "()V", (void *)terminateQtNativeApplication },
737 { "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
738 { "updateApplicationState", "(I)V", (void *)updateApplicationState },
739 { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },
740 { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent },
741 { "onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind },
742 { "updateLocale", "()V", (void *)updateLocale },
743};
744
745#define FIND_AND_CHECK_CLASS(CLASS_NAME) clazz
746 = env->FindClass(CLASS_NAME); if
747 (!clazz) {
748 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME);
749 return false; \
750}
751
752#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) VAR
753 = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); if
754 (!VAR) {
755 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
756 return false; \
757}
758
759#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) VAR
760 = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); if
761 (!VAR) {
762 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE);
763 return false; \
764}
765
766#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) VAR
767 = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); if
768 (!VAR) {
769 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE);
770 return false; \
771}
772
773#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) VAR
774 = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); if
775 (!VAR) {
776 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE);
777 return false; \
778}
779
780Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
781
782static bool registerNatives(QJniEnvironment &env)
783{
784 bool success = env.registerNativeMethods(m_applicationClass,
785 methods, sizeof(methods) / sizeof(methods[0]));
786 success &= env.registerNativeMethods(
787 QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(),
788 {
789 Q_JNI_NATIVE_METHOD(handleLayoutSizeChanged),
790 Q_JNI_NATIVE_METHOD(handleOrientationChanged),
791 Q_JNI_NATIVE_METHOD(handleRefreshRateChanged),
792 Q_JNI_NATIVE_METHOD(handleScreenAdded),
793 Q_JNI_NATIVE_METHOD(handleScreenChanged),
794 Q_JNI_NATIVE_METHOD(handleScreenRemoved),
795 Q_JNI_NATIVE_METHOD(handleUiDarkModeChanged),
796 Q_JNI_NATIVE_METHOD(handleScreenDensityChanged)
797 });
798
799 success = success
800 && QtAndroidInput::registerNatives(env)
801 && QtAndroidMenu::registerNatives(env)
802#if QT_CONFIG(accessibility)
803 && QtAndroidAccessibility::registerNatives(env)
804#endif
805 && QtAndroidDialogHelpers::registerNatives(env)
806#if QT_CONFIG(clipboard)
807 && QAndroidPlatformClipboard::registerNatives(env)
808#endif
809 && QAndroidPlatformWindow::registerNatives(env)
810 && QtAndroidWindowEmbedding::registerNatives(env)
811 && AndroidBackendRegister::registerNatives()
812 && QAndroidModelIndexProxy::registerNatives(env)
813 && QAndroidItemModelProxy::registerAbstractNatives(env)
814 && QAndroidItemModelProxy::registerProxyNatives(env);
815
816 return success;
817}
818
819static bool initJavaReferences(QJniEnvironment &env)
820{
821 if (m_applicationClass)
822 return true;
823
824 jclass clazz;
825 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNative");
826 m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
827
828 jmethodID methodID;
829 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
830
831 jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
832 if (!contextObject) {
833 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
834 contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
835 }
836
837 if (!contextObject) {
838 __android_log_print(ANDROID_LOG_FATAL,"Qt", "Failed to get Activity or Service object");
839 return false;
840 }
841 const auto releaseContextObject = qScopeGuard([&env, contextObject]{
842 env->DeleteLocalRef(contextObject);
843 });
844
845 FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
846 GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
847 m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
848 m_assetManager = AAssetManager_fromJava(env.jniEnv(), m_assets);
849
850 GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
851 m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
852
853 FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
854 m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
855 GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass,
856 "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
857 FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
858 jfieldID fieldId;
859 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
860 m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
861 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
862 m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
863
864 FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
865 m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
866 GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
867 m_bitmapDrawableClass,
868 "<init>", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
869
870 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtActivityBase");
871 m_qtActivityClass = static_cast<jclass>(env->NewGlobalRef(clazz));
872 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtServiceBase");
873 m_qtServiceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
874
875 // The current thread will be the Qt thread, name it accordingly
876 QThread::currentThread()->setObjectName("QtMainLoopThread");
877
878 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
879
880 return true;
881}
882
883QT_END_NAMESPACE
884
885Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM */*vm*/, void */*reserved*/)
886{
887 static bool initialized = false;
888 if (initialized)
889 return JNI_VERSION_1_6;
890 initialized = true;
891
892 QT_USE_NAMESPACE
893
894 QJniEnvironment env;
895 if (!env.isValid()) {
896 __android_log_print(ANDROID_LOG_FATAL, "Qt", "Failed to initialize the JNI Environment");
897 return JNI_ERR;
898 }
899
900 if (!initJavaReferences(env))
901 return JNI_ERR;
902
903 if (!registerNatives(env)) {
904 __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
905 return JNI_ERR;
906 }
907
908 __android_log_print(ANDROID_LOG_INFO, "Qt", "Qt platform plugin started");
909 return JNI_VERSION_1_6;
910}
#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 Main m_main
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 double m_density
static jobject m_assets
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()
QWindow * topLevelAt(const QPoint &p) const override
Return the given top level window for a given position.
\inmodule QtCore\reentrant
Definition qpoint.h:29
const char * classErrorMsgFmt()
void setViewVisibility(jobject view, bool visible)
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
const char * qtTagText()
jobject assets()
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()
QString deviceName()
bool blockEventLoopsWhenSuspended()
double pixelDensity()
QWindow * windowFromId(int windowId)
jclass applicationClass()
void notifyNativePluginIntegrationReady(bool ready)
AAssetManager * assetManager()
bool isQtApplication()
const char * methodErrorMsgFmt()
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")