Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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 "androidjniinput.h"
14#include "androidjnimain.h"
15#include "androidjnimenu.h"
23
24#include <android/api-level.h>
25#include <android/asset_manager_jni.h>
26#include <android/bitmap.h>
27
28#include <QtCore/private/qjnihelpers_p.h>
29#include <QtCore/qbasicatomic.h>
30#include <QtCore/qjnienvironment.h>
31#include <QtCore/qjniobject.h>
32#include <QtCore/qprocess.h>
33#include <QtCore/qresource.h>
34#include <QtCore/qscopeguard.h>
35#include <QtCore/qthread.h>
36#include <QtGui/private/qguiapplication_p.h>
37#include <QtGui/private/qhighdpiscaling_p.h>
38
39#include <qpa/qwindowsysteminterface.h>
40
41
42using namespace Qt::StringLiterals;
43
45
46static JavaVM *m_javaVM = nullptr;
47static jclass m_applicationClass = nullptr;
48static jobject m_classLoaderObject = nullptr;
49static jmethodID m_loadClassMethodID = nullptr;
50static AAssetManager *m_assetManager = nullptr;
51static jobject m_assets = nullptr;
52static jobject m_resourcesObj = nullptr;
53
54static jclass m_qtActivityClass = nullptr;
55static jclass m_qtServiceClass = nullptr;
56
57static QtJniTypes::QtActivityDelegateBase m_activityDelegate = nullptr;
58static QtJniTypes::QtInputDelegate m_inputDelegate = nullptr;
59
62
63static jclass m_bitmapClass = nullptr;
64static jmethodID m_createBitmapMethodID = nullptr;
65static jobject m_ARGB_8888_BitmapConfigValue = nullptr;
66static jobject m_RGB_565_BitmapConfigValue = nullptr;
67
68static jclass m_bitmapDrawableClass = nullptr;
69static jmethodID m_bitmapDrawableConstructorMethodID = nullptr;
70
71extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
72static Main m_main = nullptr;
73static void *m_mainLibraryHnd = nullptr;
74static QList<QByteArray> m_applicationParams;
76
77
79
82static double m_scaledDensity = 0;
83static double m_density = 1.0;
84
87
88
89
90static const char m_qtTag[] = "Qt";
91static const char m_classErrorMsg[] = "Can't find class \"%s\"";
92static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
93
95
96Q_DECLARE_JNI_CLASS(QtEmbeddedDelegateFactory, "org/qtproject/qt/android/QtEmbeddedDelegateFactory")
97
98namespace QtAndroid
99{
104
121
126
127 QWindow *topLevelWindowAt(const QPoint &globalPos)
128 {
130 ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos)
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 {
152 }
153
155 {
157 }
158
160 {
161 return m_scaledDensity;
162 }
163
165 {
166 return m_density;
167 }
168
169 JavaVM *javaVM()
170 {
171 return m_javaVM;
172 }
173
174 AAssetManager *assetManager()
175 {
176 return m_assetManager;
177 }
178
180 {
181 return m_applicationClass;
182 }
183
184 // TODO move calls from here to where they logically belong
186 {
187 qtActivityDelegate().callMethod<void>("setSystemUiVisibility", jint(uiVisibility));
188 }
189
190 // FIXME: avoid direct access to QtActivityDelegate
191 QtJniTypes::QtActivityDelegateBase qtActivityDelegate()
192 {
193 using namespace QtJniTypes;
194 if (!m_activityDelegate.isValid()) {
195 if (isQtApplication()) {
197 m_activityDelegate = context.callMethod<QtActivityDelegateBase>("getActivityDelegate");
198 } else {
199 m_activityDelegate = QJniObject::callStaticMethod<QtActivityDelegateBase>(
200 Traits<QtEmbeddedDelegateFactory>::className(),
201 "getActivityDelegate",
203 }
204 }
205
206 return m_activityDelegate;
207 }
208
209 QtJniTypes::QtInputDelegate qtInputDelegate()
210 {
211 if (!m_inputDelegate.isValid()) {
212 m_inputDelegate = qtActivityDelegate().callMethod<QtJniTypes::QtInputDelegate>(
213 "getInputDelegate");
214 }
215
216 return m_inputDelegate;
217 }
218
220 {
221 // Returns true if the app is a Qt app, i.e. Qt controls the whole app and
222 // the Activity/Service is created by Qt. Returns false if instead Qt is
223 // embedded into a native Android app, where the Activity/Service is created
224 // by the user, outside of Qt, and Qt content is added as a view.
225 JNIEnv *env = QJniEnvironment::getJniEnv();
226 auto activity = QtAndroidPrivate::activity();
227 if (activity.isValid())
228 return env->IsInstanceOf(activity.object(), m_qtActivityClass);
229 auto service = QtAndroidPrivate::service();
230 if (service.isValid())
231 return env->IsInstanceOf(QtAndroidPrivate::service().object(), m_qtServiceClass);
232 // return true as default as Qt application is our default use case.
233 // famous last words: we should not end up here
234 return true;
235 }
236
237 void notifyAccessibilityLocationChange(uint accessibilityObjectId)
238 {
239 qtActivityDelegate().callMethod<void>("notifyLocationChange", accessibilityObjectId);
240 }
241
242 void notifyObjectHide(uint accessibilityObjectId, uint parentObjectId)
243 {
244 qtActivityDelegate().callMethod<void>("notifyObjectHide",
245 accessibilityObjectId, parentObjectId);
246 }
247
248 void notifyObjectShow(uint parentObjectId)
249 {
250 qtActivityDelegate().callMethod<void>("notifyObjectShow",
251 parentObjectId);
252 }
253
254 void notifyObjectFocus(uint accessibilityObjectId)
255 {
256 qtActivityDelegate().callMethod<void>("notifyObjectFocus", accessibilityObjectId);
257 }
258
259 void notifyValueChanged(uint accessibilityObjectId, jstring value)
260 {
261 qtActivityDelegate().callMethod<void>("notifyValueChanged", accessibilityObjectId, value);
262 }
263
264 void notifyScrolledEvent(uint accessibilityObjectId)
265 {
266 qtActivityDelegate().callMethod<void>("notifyScrolledEvent", accessibilityObjectId);
267 }
268
270 {
271 QJniObject::callStaticMethod<void>(m_applicationClass,
272 "notifyNativePluginIntegrationReady",
273 ready);
274 }
275
276 jobject createBitmap(QImage img, JNIEnv *env)
277 {
278 if (!m_bitmapClass)
279 return 0;
280
281 if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16)
282 img = std::move(img).convertToFormat(QImage::Format_RGBA8888);
283
284 jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass,
286 img.width(),
287 img.height(),
288 img.format() == QImage::Format_RGBA8888
291 if (!bitmap)
292 return 0;
293
294 AndroidBitmapInfo info;
295 if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
296 env->DeleteLocalRef(bitmap);
297 return 0;
298 }
299
300 void *pixels;
301 if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
302 env->DeleteLocalRef(bitmap);
303 return 0;
304 }
305
306 if (info.stride == uint(img.bytesPerLine())
307 && info.width == uint(img.width())
308 && info.height == uint(img.height())) {
309 memcpy(pixels, img.constBits(), info.stride * info.height);
310 } else {
311 uchar *bmpPtr = static_cast<uchar *>(pixels);
312 const unsigned width = qMin(info.width, (uint)img.width()); //should be the same
313 const unsigned height = qMin(info.height, (uint)img.height()); //should be the same
314 for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
315 memcpy(bmpPtr, img.constScanLine(y), width);
316 }
317 AndroidBitmap_unlockPixels(env, bitmap);
318 return bitmap;
319 }
320
321 jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
322 {
325 return 0;
326
327 return env->CallStaticObjectMethod(m_bitmapClass,
329 width,
330 height,
334 }
335
336 jobject createBitmapDrawable(jobject bitmap, JNIEnv *env)
337 {
339 return 0;
340
341 return env->NewObject(m_bitmapDrawableClass,
344 bitmap);
345 }
346
347 const char *classErrorMsgFmt()
348 {
349 return m_classErrorMsg;
350 }
351
352 const char *methodErrorMsgFmt()
353 {
354 return m_methodErrorMsg;
355 }
356
357 const char *qtTagText()
358 {
359 return m_qtTag;
360 }
361
363 {
364 QString manufacturer = QJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString();
365 QString model = QJniObject::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString();
366
367 return manufacturer + u' ' + model;
368 }
369
370 void setViewVisibility(jobject view, bool visible)
371 {
372 QJniObject::callStaticMethod<void>(m_applicationClass,
373 "setViewVisibility",
374 "(Landroid/view/View;Z)V",
375 view,
376 visible);
377 }
378
380 {
381 static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
382 return block;
383 }
384
385 jobject assets()
386 {
387 return m_assets;
388 }
389
390} // namespace QtAndroid
391
392static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString)
393{
394 Q_UNUSED(env)
395
399 m_mainLibraryHnd = nullptr;
400
401 const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
402
403 for (const QString &arg : argsList)
404 m_applicationParams.append(arg.toUtf8());
405
406 // Go home
408
409 //look for main()
410 if (m_applicationParams.length()) {
411 // Obtain a handle to the main library (the library that contains the main() function).
412 // This library should already be loaded, and calling dlopen() will just return a reference to it.
413 m_mainLibraryHnd = dlopen(m_applicationParams.constFirst().data(), 0);
415 qCritical() << "dlopen failed:" << dlerror();
416 return false;
417 }
418 m_main = (Main)dlsym(m_mainLibraryHnd, "main");
419 } else {
420 qWarning("No main library was specified; searching entire process (this is slow!)");
421 m_main = (Main)dlsym(RTLD_DEFAULT, "main");
422 }
423
424 if (Q_UNLIKELY(!m_main)) {
425 qCritical() << "dlsym failed:" << dlerror() << Qt::endl
426 << "Could not find main method";
427 return false;
428 }
429
430 if (sem_init(&m_exitSemaphore, 0, 0) == -1)
431 return false;
432
433 if (sem_init(&m_terminateSemaphore, 0, 0) == -1)
434 return false;
435
436 return true;
437}
438
439static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
440{
441 Q_UNUSED(env);
442 // The service must wait until the QCoreApplication starts otherwise onBind will be
443 // called too early
446}
447
448static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
449{
450 {
451 JNIEnv* env = nullptr;
452 JavaVMAttachArgs args;
453 args.version = JNI_VERSION_1_6;
454 args.name = "QtMainThread";
455 args.group = NULL;
456 JavaVM *vm = QJniEnvironment::javaVM();
457 if (vm)
458 vm->AttachCurrentThread(&env, &args);
459 }
460
461 // Register type for invokeMethod() calls.
462 qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation");
463
464 // Register resources if they are available
465 if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
466 QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
467
468 const int argc = m_applicationParams.size();
469 QVarLengthArray<char *> argv(argc + 1);
470 for (int i = 0; i < argc; i++)
471 argv[i] = m_applicationParams[i].data();
472 argv[argc] = nullptr;
473
474 startQtAndroidPluginCalled.fetchAndAddRelease(1);
475 const int ret = m_main(argc, argv.data());
476 qInfo() << "main() returned" << ret;
477
478 if (m_mainLibraryHnd) {
479 int res = dlclose(m_mainLibraryHnd);
480 if (res < 0)
481 qWarning() << "dlclose failed:" << dlerror();
482 }
483
485 QJniObject::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
486
487 sem_post(&m_terminateSemaphore);
488 sem_wait(&m_exitSemaphore);
489 sem_destroy(&m_exitSemaphore);
490
491 // We must call exit() to ensure that all global objects will be destructed
492 if (!qEnvironmentVariableIsSet("QT_ANDROID_NO_EXIT_CALL"))
493 exit(ret);
494}
495
496static void quitQtCoreApplication(JNIEnv *env, jclass /*clazz*/)
497{
498 Q_UNUSED(env);
500}
501
502static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/)
503{
504 Q_UNUSED(env);
510}
511
512static void terminateQt(JNIEnv *env, jclass /*clazz*/)
513{
514 // QAndroidEventDispatcherStopper is stopped when the user uses the task manager to kill the application
518 QAndroidEventDispatcherStopper::instance()->goingToStop(false);
519 }
520
521 if (startQtAndroidPluginCalled.loadAcquire())
522 sem_wait(&m_terminateSemaphore);
523
524 sem_destroy(&m_terminateSemaphore);
525
526 env->DeleteGlobalRef(m_applicationClass);
527 env->DeleteGlobalRef(m_classLoaderObject);
528 if (m_resourcesObj)
529 env->DeleteGlobalRef(m_resourcesObj);
530 if (m_bitmapClass)
531 env->DeleteGlobalRef(m_bitmapClass);
533 env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue);
535 env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
537 env->DeleteGlobalRef(m_bitmapDrawableClass);
538 if (m_assets)
539 env->DeleteGlobalRef(m_assets);
541 env->DeleteGlobalRef(m_qtActivityClass);
543 env->DeleteGlobalRef(m_qtServiceClass);
547 sem_post(&m_exitSemaphore);
548}
549
550static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels,
551 jint screenHeightPixels, jint availableLeftPixels,
552 jint availableTopPixels, jint availableWidthPixels,
553 jint availableHeightPixels, jdouble xdpi, jdouble ydpi,
554 jdouble scaledDensity, jdouble density, jfloat refreshRate)
555{
556 Q_UNUSED(availableLeftPixels)
557 Q_UNUSED(availableTopPixels)
558
559 m_availableWidthPixels = availableWidthPixels;
560 m_availableHeightPixels = availableHeightPixels;
561 m_scaledDensity = scaledDensity;
562 m_density = density;
563
564 const QSize screenSize(screenWidthPixels, screenHeightPixels);
565 // available geometry always starts from top left
566 const QRect availableGeometry(0, 0, availableWidthPixels, availableHeightPixels);
567 const QSize physicalSize(qRound(double(screenWidthPixels) / xdpi * 25.4),
568 qRound(double(screenHeightPixels) / ydpi * 25.4));
569
573 availableGeometry.left(), availableGeometry.top(), availableGeometry.width(),
574 availableGeometry.height(), physicalSize.width(), physicalSize.height(),
575 screenSize.width(), screenSize.height());
576 } else {
577 m_androidPlatformIntegration->setScreenSizeParameters(physicalSize, screenSize,
578 availableGeometry);
579 m_androidPlatformIntegration->setRefreshRate(refreshRate);
580 }
581}
582Q_DECLARE_JNI_NATIVE_METHOD(setDisplayMetrics)
583
584static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/)
585{
587 return;
588
589 if (QGuiApplication::instance() != nullptr) {
590 const auto tlw = QGuiApplication::topLevelWindows();
591 for (QWindow *w : tlw) {
592
593 // Skip non-platform windows, e.g., offscreen windows.
594 if (!w->handle())
595 continue;
596
597 QRect availableGeometry = w->screen()->availableGeometry();
598 if (w->geometry().width() > 0 && w->geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
600 }
601 }
602}
603
604static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
605{
609 return;
610 }
611
612 // We're about to call user code from the Android thread, since we don't know
613 //the side effects we'll unlock first!
614 lock.unlock();
617 else if (state == Qt::ApplicationInactive)
619 lock.relock();
621 return;
622
624 // NOTE: sometimes we will receive two consecutive suspended notifications,
625 // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents()
626 // will deadlock since the dispatcher has been stopped in the first suspended notification.
627 // To avoid the deadlock we simply return if we found the event dispatcher has been stopped.
629 return;
630
631 // Don't send timers and sockets events anymore if we are going to hide all windows
632 QAndroidEventDispatcherStopper::instance()->goingToStop(true);
636 } else {
639 QAndroidEventDispatcherStopper::instance()->goingToStop(false);
640 }
641}
642
643static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation)
644{
645 // Array of orientations rotated in 90 degree increments, counterclockwise
646 // (same direction as Android measures angles)
647 static const Qt::ScreenOrientation orientations[] = {
652 };
653
654 // The Android API defines the following constants:
655 // ROTATION_0 : 0
656 // ROTATION_90 : 1
657 // ROTATION_180 : 2
658 // ROTATION_270 : 3
659 // ORIENTATION_PORTRAIT : 1
660 // ORIENTATION_LANDSCAPE : 2
661
662 // and newRotation is how much the current orientation is rotated relative to nativeOrientation
663
664 // which means that we can be really clever here :)
665 Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4];
666 Qt::ScreenOrientation native = orientations[nativeOrientation - 1];
667
668 QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
672 // Use invokeMethod to keep the certain order of the "geometry change"
673 // and "orientation change" event handling.
674 if (screen) {
676 Q_ARG(Qt::ScreenOrientation, screenOrientation));
677 }
678 }
679}
680Q_DECLARE_JNI_NATIVE_METHOD(handleOrientationChanged)
681
682static void handleRefreshRateChanged(JNIEnv */*env*/, jclass /*cls*/, jfloat refreshRate)
683{
685 m_androidPlatformIntegration->setRefreshRate(refreshRate);
686}
687Q_DECLARE_JNI_NATIVE_METHOD(handleRefreshRateChanged)
688
689static void handleScreenAdded(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
690{
692 m_androidPlatformIntegration->handleScreenAdded(displayId);
693}
694Q_DECLARE_JNI_NATIVE_METHOD(handleScreenAdded)
695
696static void handleScreenChanged(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
697{
699 m_androidPlatformIntegration->handleScreenChanged(displayId);
700}
701Q_DECLARE_JNI_NATIVE_METHOD(handleScreenChanged)
702
703static void handleScreenRemoved(JNIEnv */*env*/, jclass /*cls*/, jint displayId)
704{
706 m_androidPlatformIntegration->handleScreenRemoved(displayId);
707}
708Q_DECLARE_JNI_NATIVE_METHOD(handleScreenRemoved)
709
710static void handleUiDarkModeChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newUiMode)
711{
713 (newUiMode == 1 ) ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light);
714}
715Q_DECLARE_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
716
717static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
718 jint requestCode,
719 jint resultCode,
720 jobject data)
721{
722 QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data);
723}
724
725static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data)
726{
728}
729
730static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
731{
733}
734
735static JNINativeMethod methods[] = {
736 { "startQtAndroidPlugin", "(Ljava/lang/String;)Z", (void *)startQtAndroidPlugin },
737 { "startQtApplication", "()V", (void *)startQtApplication },
738 { "quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin },
739 { "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication },
740 { "terminateQt", "()V", (void *)terminateQt },
741 { "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
742 { "updateWindow", "()V", (void *)updateWindow },
743 { "updateApplicationState", "(I)V", (void *)updateApplicationState },
744 { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },
745 { "onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent },
746 { "onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind }
747};
748
749#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
750clazz = env->FindClass(CLASS_NAME); \
751if (!clazz) { \
752 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
753 return false; \
754}
755
756#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
757VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
758if (!VAR) { \
759 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
760 return false; \
761}
762
763#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
764VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
765if (!VAR) { \
766 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
767 return false; \
768}
769
770#define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
771VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
772if (!VAR) { \
773 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
774 return false; \
775}
776
777#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
778VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
779if (!VAR) { \
780 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
781 return false; \
782}
783
784Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtDisplayManager")
785
787{
788 jclass clazz;
789 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtNative");
790 m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
791
792 if (!env.registerNativeMethods(m_applicationClass,
793 methods, sizeof(methods) / sizeof(methods[0]))) {
794 __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
795 return false;
796 }
797
798 bool success = env.registerNativeMethods(
799 QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(),
800 {
801 Q_JNI_NATIVE_METHOD(setDisplayMetrics),
802 Q_JNI_NATIVE_METHOD(handleOrientationChanged),
803 Q_JNI_NATIVE_METHOD(handleRefreshRateChanged),
804 Q_JNI_NATIVE_METHOD(handleScreenAdded),
805 Q_JNI_NATIVE_METHOD(handleScreenChanged),
806 Q_JNI_NATIVE_METHOD(handleScreenRemoved),
807 Q_JNI_NATIVE_METHOD(handleUiDarkModeChanged)
808 });
809
810 if (!success) {
811 qCritical() << "QtDisplayManager: registerNativeMethods() failed";
812 return JNI_FALSE;
813 }
814
815 jmethodID methodID;
816 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
817 jobject contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
818 if (!contextObject) {
819 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
820 contextObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
821 }
822
823 if (!contextObject) {
824 __android_log_print(ANDROID_LOG_FATAL,"Qt", "Failed to get Activity or Service object");
825 return false;
826 }
827 const auto releaseContextObject = qScopeGuard([&env, contextObject]{
828 env->DeleteLocalRef(contextObject);
829 });
830
831 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;");
832 m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID));
833 clazz = env->GetObjectClass(m_classLoaderObject);
834 GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
835
836 FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
837 GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
838 m_assets = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
839 m_assetManager = AAssetManager_fromJava(env.jniEnv(), m_assets);
840
841 GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
842 m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(contextObject, methodID));
843
844 FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
845 m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
847 "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
848 FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
849 jfieldID fieldId;
850 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
851 m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
852 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
853 m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
854
855 FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
856 m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
859 "<init>", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
860
861 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtActivityBase");
862 m_qtActivityClass = static_cast<jclass>(env->NewGlobalRef(clazz));
863 FIND_AND_CHECK_CLASS("org/qtproject/qt/android/QtServiceBase");
864 m_qtServiceClass = static_cast<jclass>(env->NewGlobalRef(clazz));
865
866 return true;
867}
868
870
871Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
872{
873 static bool initialized = false;
874 if (initialized)
875 return JNI_VERSION_1_6;
876 initialized = true;
877
879 m_javaVM = vm;
880 QJniEnvironment env;
881 if (!env.isValid()) {
882 m_javaVM = nullptr;
883 __android_log_print(ANDROID_LOG_FATAL, "Qt", "Failed to initialize the JNI Environment");
884 return -1;
885 }
886
887 if (!registerNatives(env)
895 __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
896 return -1;
897 }
899
900 // attach qt main thread data to this thread
901 QThread::currentThread()->setObjectName("QtMainLoopThread");
902 __android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
903 return JNI_VERSION_1_6;
904}
static int m_pendingApplicationState
static jclass m_qtServiceClass
static jobject m_ARGB_8888_BitmapConfigValue
#define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE)
static jmethodID m_bitmapDrawableConstructorMethodID
static void setDisplayMetrics(JNIEnv *, jclass, jint screenWidthPixels, jint screenHeightPixels, jint availableLeftPixels, jint availableTopPixels, jint availableWidthPixels, jint availableHeightPixels, jdouble xdpi, jdouble ydpi, jdouble scaledDensity, jdouble density, jfloat refreshRate)
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
static AndroidAssetsFileEngineHandler * m_androidAssetsFileEngineHandler
static double m_density
static Q_CONSTINIT QBasicAtomicInt startQtAndroidPluginCalled
static QBasicMutex m_platformMutex
static jmethodID m_loadClassMethodID
static void * m_mainLibraryHnd
static void waitForServiceSetup(JNIEnv *env, jclass)
static sem_t m_exitSemaphore
static jobject m_assets
static JNINativeMethod methods[]
static void handleScreenChanged(JNIEnv *, jclass, jint displayId)
static jclass m_bitmapDrawableClass
QT_END_NAMESPACE Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *)
static void onNewIntent(JNIEnv *env, jclass, jobject data)
#define FIND_AND_CHECK_CLASS(CLASS_NAME)
static QtJniTypes::QtInputDelegate m_inputDelegate
#define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
static jobject m_classLoaderObject
int(* Main)(int, char **)
static jmethodID m_createBitmapMethodID
static void handleUiDarkModeChanged(JNIEnv *, jobject, jint newUiMode)
static void handleRefreshRateChanged(JNIEnv *, jclass, jfloat refreshRate)
static void startQtApplication(JNIEnv *, jclass)
static int m_availableHeightPixels
static void updateApplicationState(JNIEnv *, jobject, jint state)
static sem_t m_terminateSemaphore
static jclass m_bitmapClass
static void handleOrientationChanged(JNIEnv *, jobject, jint newRotation, jint nativeOrientation)
static jobject m_resourcesObj
static jobject m_RGB_565_BitmapConfigValue
static jclass m_applicationClass
static void terminateQt(JNIEnv *env, jclass)
static jboolean startQtAndroidPlugin(JNIEnv *env, jobject, jstring paramsString)
static const char m_classErrorMsg[]
static jclass m_qtActivityClass
static QtJniTypes::QtActivityDelegateBase m_activityDelegate
static int m_availableWidthPixels
static QT_BEGIN_NAMESPACE JavaVM * m_javaVM
static void handleScreenRemoved(JNIEnv *, jclass, jint displayId)
static double m_scaledDensity
static QAndroidPlatformIntegration * m_androidPlatformIntegration
static const char m_methodErrorMsg[]
static void quitQtAndroidPlugin(JNIEnv *env, jclass)
static AndroidContentFileEngineHandler * m_androidContentFileEngineHandler
static void quitQtCoreApplication(JNIEnv *env, jclass)
static const char m_qtTag[]
static QList< QByteArray > m_applicationParams
static jobject onBind(JNIEnv *, jclass, jobject intent)
static void onActivityResult(JNIEnv *, jclass, jint requestCode, jint resultCode, jobject data)
static Main m_main
static AAssetManager * m_assetManager
static void updateWindow(JNIEnv *, jobject)
static void handleScreenAdded(JNIEnv *, jclass, jint displayId)
static QAndroidEventDispatcherStopper * instance()
static bool registerNatives(QJniEnvironment &env)
static void setDefaultDisplayMetrics(int availableLeft, int availableTop, int availableWidth, int availableHeight, int physicalWidth, int physicalHeight, int screenWidth, int screenHeight)
static void updateColorScheme(Qt::ColorScheme colorScheme)
static void setScreenOrientation(Qt::ScreenOrientation currentOrientation, Qt::ScreenOrientation nativeOrientation)
static bool registerNatives(QJniEnvironment &env)
static void quit()
\threadsafe
static QCoreApplication * instance() noexcept
Returns a pointer to the application's QCoreApplication (or QGuiApplication/QApplication) instance.
static bool setCurrent(const QString &path)
Sets the application's current working directory to path.
Definition qdir.cpp:2030
static QString homePath()
Returns the absolute path of the user's home directory.
Definition qdir.cpp:2103
\inmodule QtCore
Definition qfile.h:93
static QWindowList topLevelWindows()
Returns a list of the top-level windows in the application.
\inmodule QtGui
Definition qimage.h:37
Format
The following image formats are available in Qt.
Definition qimage.h:41
@ Format_RGBA8888
Definition qimage.h:59
@ Format_RGB16
Definition qimage.h:49
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr int top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:176
constexpr int left() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:173
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
The QRegion class specifies a clip region for a painter.
Definition qregion.h:27
static bool registerResource(const QString &rccFilename, const QString &resourceRoot=QString())
Registers the resource with the given rccFileName at the location in the resource tree specified by m...
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QThread * currentThread()
Definition qthread.cpp:1039
static bool handleExposeEvent(QWindow *window, const QRegion &region)
static void handleApplicationStateChanged(Qt::ApplicationState newState, bool forcePropagate=false)
\inmodule QtGui
Definition qwindow.h:63
else opt state
[0]
static bool registerNatives()
Combined button and popup list for selecting options.
bool registerNatives(QJniEnvironment &env)
bool registerNatives(QJniEnvironment &env)
bool registerNatives(QJniEnvironment &env)
bool registerNatives(QJniEnvironment &env)
Q_CORE_EXPORT jobject callOnBindListener(jobject intent)
Q_CORE_EXPORT void handleResume()
Q_CORE_EXPORT QtJniTypes::Activity activity()
Q_CORE_EXPORT void handleNewIntent(JNIEnv *env, jobject intent)
Q_CORE_EXPORT void handlePause()
Q_CORE_EXPORT QtJniTypes::Service service()
Q_CORE_EXPORT void handleActivityResult(jint requestCode, jint resultCode, jobject data)
Q_CORE_EXPORT void waitForServiceSetup()
bool registerNatives(QJniEnvironment &env)
const char * classErrorMsgFmt()
void notifyAccessibilityLocationChange(uint accessibilityObjectId)
void setViewVisibility(jobject view, bool visible)
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
const char * qtTagText()
jobject assets()
QBasicMutex * platformInterfaceMutex()
jobject createBitmap(QImage img, JNIEnv *env)
QWindow * topLevelWindowAt(const QPoint &globalPos)
double scaledDensity()
QtJniTypes::QtActivityDelegateBase qtActivityDelegate()
QAndroidPlatformIntegration * androidPlatformIntegration()
QtJniTypes::QtInputDelegate qtInputDelegate()
QString deviceName()
int availableWidthPixels()
bool blockEventLoopsWhenSuspended()
void setSystemUiVisibility(SystemUiVisibility uiVisibility)
double pixelDensity()
QWindow * windowFromId(int windowId)
jclass applicationClass()
jobject createBitmapDrawable(jobject bitmap, JNIEnv *env)
void notifyNativePluginIntegrationReady(bool ready)
int availableHeightPixels()
AAssetManager * assetManager()
bool isQtApplication()
const char * methodErrorMsgFmt()
ScreenOrientation
Definition qnamespace.h:271
@ InvertedLandscapeOrientation
Definition qnamespace.h:276
@ InvertedPortraitOrientation
Definition qnamespace.h:275
@ LandscapeOrientation
Definition qnamespace.h:274
@ PortraitOrientation
Definition qnamespace.h:273
ApplicationState
Definition qnamespace.h:262
@ ApplicationSuspended
Definition qnamespace.h:263
@ ApplicationActive
Definition qnamespace.h:266
@ ApplicationInactive
Definition qnamespace.h:265
@ AutoConnection
QTextStream & endl(QTextStream &stream)
Writes '\n' to the stream and flushes the stream.
static void * context
#define Q_BASIC_ATOMIC_INITIALIZER(a)
#define Q_UNLIKELY(x)
#define Q_DECL_EXPORT
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define qGuiApp
bool isQtApplication()
Definition qiosglobal.mm:20
#define qCritical
Definition qlogging.h:167
#define qInfo
Definition qlogging.h:165
#define qWarning
Definition qlogging.h:166
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLfloat GLfloat GLfloat w
[0]
GLint GLsizei GLsizei height
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei width
GLint GLenum GLsizei GLsizei GLsizei GLint GLenum GLenum const void * pixels
GLint GLsizei GLsizei GLenum format
GLint y
GLuint res
GLint void * img
Definition qopenglext.h:233
GLsizei GLfixed GLfixed GLfixed GLfixed const GLubyte * bitmap
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
SSL_CTX int void * arg
QT_BEGIN_NAMESPACE Q_DECLARE_JNI_CLASS(Environment, "android/os/Environment")
#define QStringLiteral(str)
QScreen * screen
[1]
Definition main.cpp:29
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
unsigned char uchar
Definition qtypes.h:32
unsigned int uint
Definition qtypes.h:34
QSqlQueryModel * model
[16]
QReadWriteLock lock
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QHostInfo info
[0]
QQuickView * view
[0]
char * toString(const MyType &t)
[31]
QJSValueList args
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...