1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
4 ** Copyright (C) 2016 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of the plugins of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** Commercial License Usage
11 ** Licensees holding valid commercial Qt licenses may use this file in
12 ** accordance with the commercial license agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and The Qt Company. For licensing terms
15 ** and conditions see https://www.qt.io/terms-conditions. For further
16 ** information use the contact form at https://www.qt.io/contact-us.
17 **
18 ** GNU Lesser General Public License Usage
19 ** Alternatively, this file may be used under the terms of the GNU Lesser
20 ** General Public License version 3 as published by the Free Software
21 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
22 ** packaging of this file. Please review the following information to
23 ** ensure the GNU Lesser General Public License version 3 requirements
24 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25 **
26 ** GNU General Public License Usage
27 ** Alternatively, this file may be used under the terms of the GNU
28 ** General Public License version 2.0 or (at your option) the GNU General
29 ** Public license version 3 or any later version approved by the KDE Free
30 ** Qt Foundation. The licenses are as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32 ** included in the packaging of this file. Please review the following
33 ** information to ensure the GNU General Public License requirements will
34 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35 ** https://www.gnu.org/licenses/gpl-3.0.html.
36 **
37 ** $QT_END_LICENSE$
38 **
39 ****************************************************************************/
40
41 #include <dlfcn.h>
42 #include <pthread.h>
43 #include <semaphore.h>
44 #include <qplugin.h>
45 #include <qdebug.h>
46
47 #include "androidjnimain.h"
48 #include "androidjniaccessibility.h"
49 #include "androidjniinput.h"
50 #include "androidjniclipboard.h"
51 #include "androidjnimenu.h"
52 #include "androidcontentfileengine.h"
53 #include "androiddeadlockprotector.h"
54 #include "qandroidplatformdialoghelpers.h"
55 #include "qandroidplatformintegration.h"
56 #include "qandroidassetsfileenginehandler.h"
57
58 #include <android/bitmap.h>
59 #include <android/asset_manager_jni.h>
60 #include "qandroideventdispatcher.h"
61 #include <android/api-level.h>
62
63 #include <QtCore/qresource.h>
64 #include <QtCore/qthread.h>
65 #include <QtCore/private/qjnihelpers_p.h>
66 #include <QtCore/private/qjni_p.h>
67 #include <QtGui/private/qguiapplication_p.h>
68 #include <QtGui/private/qhighdpiscaling_p.h>
69
70 #include <qpa/qwindowsysteminterface.h>
71
72 QT_BEGIN_NAMESPACE
73
74 static JavaVM *m_javaVM = nullptr;
75 static jclass m_applicationClass = nullptr;
76 static jobject m_classLoaderObject = nullptr;
77 static jmethodID m_loadClassMethodID = nullptr;
78 static AAssetManager *m_assetManager = nullptr;
79 static jobject m_assets = nullptr;
80 static jobject m_resourcesObj = nullptr;
81 static jobject m_activityObject = nullptr;
82 static jmethodID m_createSurfaceMethodID = nullptr;
83 static jobject m_serviceObject = nullptr;
84 static jmethodID m_setSurfaceGeometryMethodID = nullptr;
85 static jmethodID m_destroySurfaceMethodID = nullptr;
86
87 static int m_pendingApplicationState = -1;
88 static QBasicMutex m_platformMutex;
89
90 static jclass m_bitmapClass = nullptr;
91 static jmethodID m_createBitmapMethodID = nullptr;
92 static jobject m_ARGB_8888_BitmapConfigValue = nullptr;
93 static jobject m_RGB_565_BitmapConfigValue = nullptr;
94
95 static bool m_statusBarShowing = true;
96
97 static jclass m_bitmapDrawableClass = nullptr;
98 static jmethodID m_bitmapDrawableConstructorMethodID = nullptr;
99
100 extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application
101 static Main m_main = nullptr;
102 static void *m_mainLibraryHnd = nullptr;
103 static QList<QByteArray> m_applicationParams;
104 static sem_t m_exitSemaphore, m_terminateSemaphore;
105
106 QHash<int, AndroidSurfaceClient *> m_surfaces;
107
108 static QBasicMutex m_surfacesMutex;
109 static int m_surfaceId = 1;
110
111
112 static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
113
114 static int m_desktopWidthPixels = 0;
115 static int m_desktopHeightPixels = 0;
116 static double m_scaledDensity = 0;
117 static double m_density = 1.0;
118
119 static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr;
120 static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr;
121
122
123
124 static const char m_qtTag[] = "Qt";
125 static const char m_classErrorMsg[] = "Can't find class \"%s\"";
126 static const char m_methodErrorMsg[] = "Can't find method \"%s%s\"";
127
128 namespace QtAndroid
129 {
platformInterfaceMutex()130 QBasicMutex *platformInterfaceMutex()
131 {
132 return &m_platformMutex;
133 }
134
setAndroidPlatformIntegration(QAndroidPlatformIntegration * androidPlatformIntegration)135 void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration)
136 {
137 m_androidPlatformIntegration = androidPlatformIntegration;
138
139 // flush the pending state if necessary.
140 if (m_androidPlatformIntegration && (m_pendingApplicationState != -1))
141 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(m_pendingApplicationState));
142
143 m_pendingApplicationState = -1;
144 }
145
androidPlatformIntegration()146 QAndroidPlatformIntegration *androidPlatformIntegration()
147 {
148 return m_androidPlatformIntegration;
149 }
150
topLevelWindowAt(const QPoint & globalPos)151 QWindow *topLevelWindowAt(const QPoint &globalPos)
152 {
153 return m_androidPlatformIntegration
154 ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos)
155 : 0;
156 }
157
desktopWidthPixels()158 int desktopWidthPixels()
159 {
160 return m_desktopWidthPixels;
161 }
162
desktopHeightPixels()163 int desktopHeightPixels()
164 {
165 return m_desktopHeightPixels;
166 }
167
scaledDensity()168 double scaledDensity()
169 {
170 return m_scaledDensity;
171 }
172
pixelDensity()173 double pixelDensity()
174 {
175 return m_density;
176 }
177
javaVM()178 JavaVM *javaVM()
179 {
180 return m_javaVM;
181 }
182
assetManager()183 AAssetManager *assetManager()
184 {
185 return m_assetManager;
186 }
187
applicationClass()188 jclass applicationClass()
189 {
190 return m_applicationClass;
191 }
192
activity()193 jobject activity()
194 {
195 return m_activityObject;
196 }
197
service()198 jobject service()
199 {
200 return m_serviceObject;
201 }
202
showStatusBar()203 void showStatusBar()
204 {
205 if (m_statusBarShowing)
206 return;
207
208 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "setFullScreen", "(Z)V", false);
209 m_statusBarShowing = true;
210 }
211
hideStatusBar()212 void hideStatusBar()
213 {
214 if (!m_statusBarShowing)
215 return;
216
217 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "setFullScreen", "(Z)V", true);
218 m_statusBarShowing = false;
219 }
220
notifyAccessibilityLocationChange()221 void notifyAccessibilityLocationChange()
222 {
223 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange");
224 }
225
notifyObjectHide(uint accessibilityObjectId)226 void notifyObjectHide(uint accessibilityObjectId)
227 {
228 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId);
229 }
230
notifyObjectFocus(uint accessibilityObjectId)231 void notifyObjectFocus(uint accessibilityObjectId)
232 {
233 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId);
234 }
235
createBitmap(QImage img,JNIEnv * env)236 jobject createBitmap(QImage img, JNIEnv *env)
237 {
238 if (!m_bitmapClass)
239 return 0;
240
241 if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16)
242 img = img.convertToFormat(QImage::Format_RGBA8888);
243
244 jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass,
245 m_createBitmapMethodID,
246 img.width(),
247 img.height(),
248 img.format() == QImage::Format_RGBA8888
249 ? m_ARGB_8888_BitmapConfigValue
250 : m_RGB_565_BitmapConfigValue);
251 if (!bitmap)
252 return 0;
253
254 AndroidBitmapInfo info;
255 if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) {
256 env->DeleteLocalRef(bitmap);
257 return 0;
258 }
259
260 void *pixels;
261 if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) {
262 env->DeleteLocalRef(bitmap);
263 return 0;
264 }
265
266 if (info.stride == uint(img.bytesPerLine())
267 && info.width == uint(img.width())
268 && info.height == uint(img.height())) {
269 memcpy(pixels, img.constBits(), info.stride * info.height);
270 } else {
271 uchar *bmpPtr = static_cast<uchar *>(pixels);
272 const unsigned width = qMin(info.width, (uint)img.width()); //should be the same
273 const unsigned height = qMin(info.height, (uint)img.height()); //should be the same
274 for (unsigned y = 0; y < height; y++, bmpPtr += info.stride)
275 memcpy(bmpPtr, img.constScanLine(y), width);
276 }
277 AndroidBitmap_unlockPixels(env, bitmap);
278 return bitmap;
279 }
280
createBitmap(int width,int height,QImage::Format format,JNIEnv * env)281 jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env)
282 {
283 if (format != QImage::Format_RGBA8888
284 && format != QImage::Format_RGB16)
285 return 0;
286
287 return env->CallStaticObjectMethod(m_bitmapClass,
288 m_createBitmapMethodID,
289 width,
290 height,
291 format == QImage::Format_RGB16
292 ? m_RGB_565_BitmapConfigValue
293 : m_ARGB_8888_BitmapConfigValue);
294 }
295
createBitmapDrawable(jobject bitmap,JNIEnv * env)296 jobject createBitmapDrawable(jobject bitmap, JNIEnv *env)
297 {
298 if (!bitmap || !m_bitmapDrawableClass || !m_resourcesObj)
299 return 0;
300
301 return env->NewObject(m_bitmapDrawableClass,
302 m_bitmapDrawableConstructorMethodID,
303 m_resourcesObj,
304 bitmap);
305 }
306
classErrorMsgFmt()307 const char *classErrorMsgFmt()
308 {
309 return m_classErrorMsg;
310 }
311
methodErrorMsgFmt()312 const char *methodErrorMsgFmt()
313 {
314 return m_methodErrorMsg;
315 }
316
qtTagText()317 const char *qtTagText()
318 {
319 return m_qtTag;
320 }
321
deviceName()322 QString deviceName()
323 {
324 QString manufacturer = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString();
325 QString model = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString();
326
327 return manufacturer + QLatin1Char(' ') + model;
328 }
329
createSurface(AndroidSurfaceClient * client,const QRect & geometry,bool onTop,int imageDepth)330 int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth)
331 {
332 QJNIEnvironmentPrivate env;
333 if (!env)
334 return -1;
335
336 m_surfacesMutex.lock();
337 int surfaceId = m_surfaceId++;
338 m_surfaces[surfaceId] = client;
339 m_surfacesMutex.unlock();
340
341 jint x = 0, y = 0, w = -1, h = -1;
342 if (!geometry.isNull()) {
343 x = geometry.x();
344 y = geometry.y();
345 w = std::max(geometry.width(), 1);
346 h = std::max(geometry.height(), 1);
347 }
348 env->CallStaticVoidMethod(m_applicationClass,
349 m_createSurfaceMethodID,
350 surfaceId,
351 jboolean(onTop),
352 x, y, w, h,
353 imageDepth);
354 return surfaceId;
355 }
356
insertNativeView(jobject view,const QRect & geometry)357 int insertNativeView(jobject view, const QRect &geometry)
358 {
359 m_surfacesMutex.lock();
360 const int surfaceId = m_surfaceId++;
361 m_surfaces[surfaceId] = nullptr; // dummy
362 m_surfacesMutex.unlock();
363
364 jint x = 0, y = 0, w = -1, h = -1;
365 if (!geometry.isNull())
366 geometry.getRect(&x, &y, &w, &h);
367
368 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
369 "insertNativeView",
370 "(ILandroid/view/View;IIII)V",
371 surfaceId,
372 view,
373 x,
374 y,
375 qMax(w, 1),
376 qMax(h, 1));
377
378 return surfaceId;
379 }
380
setViewVisibility(jobject view,bool visible)381 void setViewVisibility(jobject view, bool visible)
382 {
383 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
384 "setViewVisibility",
385 "(Landroid/view/View;Z)V",
386 view,
387 visible);
388 }
389
setSurfaceGeometry(int surfaceId,const QRect & geometry)390 void setSurfaceGeometry(int surfaceId, const QRect &geometry)
391 {
392 if (surfaceId == -1)
393 return;
394
395 QJNIEnvironmentPrivate env;
396 if (!env)
397 return;
398 jint x = 0, y = 0, w = -1, h = -1;
399 if (!geometry.isNull()) {
400 x = geometry.x();
401 y = geometry.y();
402 w = geometry.width();
403 h = geometry.height();
404 }
405 env->CallStaticVoidMethod(m_applicationClass,
406 m_setSurfaceGeometryMethodID,
407 surfaceId,
408 x, y, w, h);
409 }
410
411
destroySurface(int surfaceId)412 void destroySurface(int surfaceId)
413 {
414 if (surfaceId == -1)
415 return;
416
417 {
418 QMutexLocker lock(&m_surfacesMutex);
419 const auto &it = m_surfaces.find(surfaceId);
420 if (it != m_surfaces.end())
421 m_surfaces.erase(it);
422 }
423
424 QJNIEnvironmentPrivate env;
425 if (env)
426 env->CallStaticVoidMethod(m_applicationClass,
427 m_destroySurfaceMethodID,
428 surfaceId);
429 }
430
bringChildToFront(int surfaceId)431 void bringChildToFront(int surfaceId)
432 {
433 if (surfaceId == -1)
434 return;
435
436 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
437 "bringChildToFront",
438 "(I)V",
439 surfaceId);
440 }
441
bringChildToBack(int surfaceId)442 void bringChildToBack(int surfaceId)
443 {
444 if (surfaceId == -1)
445 return;
446
447 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass,
448 "bringChildToBack",
449 "(I)V",
450 surfaceId);
451 }
452
blockEventLoopsWhenSuspended()453 bool blockEventLoopsWhenSuspended()
454 {
455 static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
456 return block;
457 }
458
assets()459 jobject assets()
460 {
461 return m_assets;
462 }
463
464 } // namespace QtAndroid
465
startQtAndroidPlugin(JNIEnv * env,jobject,jstring paramsString,jstring environmentString)466 static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
467 {
468 m_androidPlatformIntegration = nullptr;
469 m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
470 m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
471 m_mainLibraryHnd = nullptr;
472 { // Set env. vars
473 const char *nativeString = env->GetStringUTFChars(environmentString, 0);
474 const QList<QByteArray> envVars = QByteArray(nativeString).split('\t');
475 env->ReleaseStringUTFChars(environmentString, nativeString);
476 for (const QByteArray &envVar : envVars) {
477 int pos = envVar.indexOf('=');
478 if (pos != -1 && ::setenv(envVar.left(pos), envVar.mid(pos + 1), 1) != 0)
479 qWarning() << "Can't set environment" << envVar;
480 }
481 }
482
483 const char *nativeString = env->GetStringUTFChars(paramsString, 0);
484 QByteArray string = nativeString;
485 env->ReleaseStringUTFChars(paramsString, nativeString);
486
487 m_applicationParams=string.split('\t');
488
489 // Go home
490 QDir::setCurrent(QDir::homePath());
491
492 //look for main()
493 if (m_applicationParams.length()) {
494 // Obtain a handle to the main library (the library that contains the main() function).
495 // This library should already be loaded, and calling dlopen() will just return a reference to it.
496 m_mainLibraryHnd = dlopen(m_applicationParams.constFirst().data(), 0);
497 if (Q_UNLIKELY(!m_mainLibraryHnd)) {
498 qCritical() << "dlopen failed:" << dlerror();
499 return false;
500 }
501 m_main = (Main)dlsym(m_mainLibraryHnd, "main");
502 } else {
503 qWarning("No main library was specified; searching entire process (this is slow!)");
504 m_main = (Main)dlsym(RTLD_DEFAULT, "main");
505 }
506
507 if (Q_UNLIKELY(!m_main)) {
508 qCritical() << "dlsym failed:" << dlerror() << Qt::endl
509 << "Could not find main method";
510 return false;
511 }
512
513 if (sem_init(&m_exitSemaphore, 0, 0) == -1)
514 return false;
515
516 if (sem_init(&m_terminateSemaphore, 0, 0) == -1)
517 return false;
518
519 return true;
520 }
521
waitForServiceSetup(JNIEnv * env,jclass)522 static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
523 {
524 Q_UNUSED(env);
525 // The service must wait until the QCoreApplication starts otherwise onBind will be
526 // called too early
527 if (m_serviceObject)
528 QtAndroidPrivate::waitForServiceSetup();
529 }
530
startQtApplication(JNIEnv *,jclass)531 static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/)
532 {
533 {
534 JNIEnv* env = nullptr;
535 JavaVMAttachArgs args;
536 args.version = JNI_VERSION_1_6;
537 args.name = "QtMainThread";
538 args.group = NULL;
539 JavaVM *vm = QtAndroidPrivate::javaVM();
540 if (vm != 0)
541 vm->AttachCurrentThread(&env, &args);
542 }
543
544 // Register resources if they are available
545 if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists())
546 QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc"));
547
548 QVarLengthArray<const char *> params(m_applicationParams.size());
549 for (int i = 0; i < m_applicationParams.size(); i++)
550 params[i] = static_cast<const char *>(m_applicationParams[i].constData());
551
552 int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
553
554 if (m_mainLibraryHnd) {
555 int res = dlclose(m_mainLibraryHnd);
556 if (res < 0)
557 qWarning() << "dlclose failed:" << dlerror();
558 }
559
560 if (m_applicationClass) {
561 qWarning("exit app 0");
562 QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
563 }
564
565 sem_post(&m_terminateSemaphore);
566 sem_wait(&m_exitSemaphore);
567 sem_destroy(&m_exitSemaphore);
568
569 // We must call exit() to ensure that all global objects will be destructed
570 exit(ret);
571 }
572
quitQtCoreApplication(JNIEnv * env,jclass)573 static void quitQtCoreApplication(JNIEnv *env, jclass /*clazz*/)
574 {
575 Q_UNUSED(env);
576 QCoreApplication::quit();
577 }
578
quitQtAndroidPlugin(JNIEnv * env,jclass)579 static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/)
580 {
581 Q_UNUSED(env);
582 m_androidPlatformIntegration = nullptr;
583 delete m_androidAssetsFileEngineHandler;
584 m_androidAssetsFileEngineHandler = nullptr;
585 delete m_androidContentFileEngineHandler;
586 m_androidContentFileEngineHandler = nullptr;
587 }
588
terminateQt(JNIEnv * env,jclass)589 static void terminateQt(JNIEnv *env, jclass /*clazz*/)
590 {
591 // QAndroidEventDispatcherStopper is stopped when the user uses the task manager to kill the application
592 if (QAndroidEventDispatcherStopper::instance()->stopped()) {
593 QAndroidEventDispatcherStopper::instance()->startAll();
594 QCoreApplication::quit();
595 QAndroidEventDispatcherStopper::instance()->goingToStop(false);
596 }
597
598 sem_wait(&m_terminateSemaphore);
599 sem_destroy(&m_terminateSemaphore);
600
601 env->DeleteGlobalRef(m_applicationClass);
602 env->DeleteGlobalRef(m_classLoaderObject);
603 if (m_resourcesObj)
604 env->DeleteGlobalRef(m_resourcesObj);
605 if (m_activityObject)
606 env->DeleteGlobalRef(m_activityObject);
607 if (m_serviceObject)
608 env->DeleteGlobalRef(m_serviceObject);
609 if (m_bitmapClass)
610 env->DeleteGlobalRef(m_bitmapClass);
611 if (m_ARGB_8888_BitmapConfigValue)
612 env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue);
613 if (m_RGB_565_BitmapConfigValue)
614 env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
615 if (m_bitmapDrawableClass)
616 env->DeleteGlobalRef(m_bitmapDrawableClass);
617 if (m_assets)
618 env->DeleteGlobalRef(m_assets);
619 m_androidPlatformIntegration = nullptr;
620 delete m_androidAssetsFileEngineHandler;
621 m_androidAssetsFileEngineHandler = nullptr;
622 sem_post(&m_exitSemaphore);
623 }
624
setSurface(JNIEnv * env,jobject,jint id,jobject jSurface,jint w,jint h)625 static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h)
626 {
627 QMutexLocker lock(&m_surfacesMutex);
628 const auto &it = m_surfaces.find(id);
629 if (it == m_surfaces.end())
630 return;
631
632 auto surfaceClient = it.value();
633 if (surfaceClient)
634 surfaceClient->surfaceChanged(env, jSurface, w, h);
635 }
636
setDisplayMetrics(JNIEnv *,jclass,jint widthPixels,jint heightPixels,jint desktopWidthPixels,jint desktopHeightPixels,jdouble xdpi,jdouble ydpi,jdouble scaledDensity,jdouble density)637 static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/,
638 jint widthPixels, jint heightPixels,
639 jint desktopWidthPixels, jint desktopHeightPixels,
640 jdouble xdpi, jdouble ydpi,
641 jdouble scaledDensity, jdouble density)
642 {
643 // Android does not give us the correct screen size for immersive mode, but
644 // the surface does have the right size
645
646 widthPixels = qMax(widthPixels, desktopWidthPixels);
647 heightPixels = qMax(heightPixels, desktopHeightPixels);
648
649 m_desktopWidthPixels = desktopWidthPixels;
650 m_desktopHeightPixels = desktopHeightPixels;
651 m_scaledDensity = scaledDensity;
652 m_density = density;
653
654 QMutexLocker lock(&m_platformMutex);
655 if (!m_androidPlatformIntegration) {
656 QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels,
657 desktopHeightPixels,
658 qRound(double(widthPixels) / xdpi * 25.4),
659 qRound(double(heightPixels) / ydpi * 25.4),
660 widthPixels,
661 heightPixels);
662 } else {
663 m_androidPlatformIntegration->setDisplayMetrics(qRound(double(widthPixels) / xdpi * 25.4),
664 qRound(double(heightPixels) / ydpi * 25.4));
665 m_androidPlatformIntegration->setScreenSize(widthPixels, heightPixels);
666 m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels);
667 }
668 }
669
updateWindow(JNIEnv *,jobject)670 static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/)
671 {
672 if (!m_androidPlatformIntegration)
673 return;
674
675 if (QGuiApplication::instance() != nullptr) {
676 const auto tlw = QGuiApplication::topLevelWindows();
677 for (QWindow *w : tlw) {
678
679 // Skip non-platform windows, e.g., offscreen windows.
680 if (!w->handle())
681 continue;
682
683 QRect availableGeometry = w->screen()->availableGeometry();
684 if (w->geometry().width() > 0 && w->geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
685 QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size())));
686 }
687 }
688
689 QAndroidPlatformScreen *screen = static_cast<QAndroidPlatformScreen *>(m_androidPlatformIntegration->screen());
690 if (screen->rasterSurfaces())
691 QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry()));
692 }
693
updateApplicationState(JNIEnv *,jobject,jint state)694 static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
695 {
696 QMutexLocker lock(&m_platformMutex);
697 if (!m_main || !m_androidPlatformIntegration) {
698 m_pendingApplicationState = state;
699 return;
700 }
701
702 // We're about to call user code from the Android thread, since we don't know
703 //the side effects we'll unlock first!
704 lock.unlock();
705 if (state == Qt::ApplicationActive)
706 QtAndroidPrivate::handleResume();
707 else if (state == Qt::ApplicationInactive)
708 QtAndroidPrivate::handlePause();
709 lock.relock();
710 if (!m_androidPlatformIntegration)
711 return;
712
713 if (state <= Qt::ApplicationInactive) {
714 // NOTE: sometimes we will receive two consecutive suspended notifications,
715 // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents()
716 // will deadlock since the dispatcher has been stopped in the first suspended notification.
717 // To avoid the deadlock we simply return if we found the event dispatcher has been stopped.
718 if (QAndroidEventDispatcherStopper::instance()->stopped())
719 return;
720
721 // Don't send timers and sockets events anymore if we are going to hide all windows
722 QAndroidEventDispatcherStopper::instance()->goingToStop(true);
723 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
724 if (state == Qt::ApplicationSuspended)
725 QAndroidEventDispatcherStopper::instance()->stopAll();
726 } else {
727 QAndroidEventDispatcherStopper::instance()->startAll();
728 QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state));
729 QAndroidEventDispatcherStopper::instance()->goingToStop(false);
730 }
731 }
732
handleOrientationChanged(JNIEnv *,jobject,jint newRotation,jint nativeOrientation)733 static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation)
734 {
735 // Array of orientations rotated in 90 degree increments, counterclockwise
736 // (same direction as Android measures angles)
737 static const Qt::ScreenOrientation orientations[] = {
738 Qt::PortraitOrientation,
739 Qt::LandscapeOrientation,
740 Qt::InvertedPortraitOrientation,
741 Qt::InvertedLandscapeOrientation
742 };
743
744 // The Android API defines the following constants:
745 // ROTATION_0 : 0
746 // ROTATION_90 : 1
747 // ROTATION_180 : 2
748 // ROTATION_270 : 3
749 // ORIENTATION_PORTRAIT : 1
750 // ORIENTATION_LANDSCAPE : 2
751
752 // and newRotation is how much the current orientation is rotated relative to nativeOrientation
753
754 // which means that we can be really clever here :)
755 Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4];
756 Qt::ScreenOrientation native = orientations[nativeOrientation - 1];
757
758 QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native);
759 QMutexLocker lock(&m_platformMutex);
760 if (m_androidPlatformIntegration) {
761 QPlatformScreen *screen = m_androidPlatformIntegration->screen();
762 QWindowSystemInterface::handleScreenOrientationChange(screen->screen(),
763 screenOrientation);
764 }
765 }
766
onActivityResult(JNIEnv *,jclass,jint requestCode,jint resultCode,jobject data)767 static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/,
768 jint requestCode,
769 jint resultCode,
770 jobject data)
771 {
772 QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data);
773 }
774
onNewIntent(JNIEnv * env,jclass,jobject data)775 static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data)
776 {
777 QtAndroidPrivate::handleNewIntent(env, data);
778 }
779
onBind(JNIEnv *,jclass,jobject intent)780 static jobject onBind(JNIEnv */*env*/, jclass /*cls*/, jobject intent)
781 {
782 return QtAndroidPrivate::callOnBindListener(intent);
783 }
784
785 static JNINativeMethod methods[] = {
786 {"startQtAndroidPlugin", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)startQtAndroidPlugin},
787 {"startQtApplication", "()V", (void *)startQtApplication},
788 {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin},
789 {"quitQtCoreApplication", "()V", (void *)quitQtCoreApplication},
790 {"terminateQt", "()V", (void *)terminateQt},
791 {"waitForServiceSetup", "()V", (void *)waitForServiceSetup},
792 {"setDisplayMetrics", "(IIIIDDDD)V", (void *)setDisplayMetrics},
793 {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface},
794 {"updateWindow", "()V", (void *)updateWindow},
795 {"updateApplicationState", "(I)V", (void *)updateApplicationState},
796 {"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged},
797 {"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult},
798 {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent},
799 {"onBind", "(Landroid/content/Intent;)Landroid/os/IBinder;", (void *)onBind}
800 };
801
802 #define FIND_AND_CHECK_CLASS(CLASS_NAME) \
803 clazz = env->FindClass(CLASS_NAME); \
804 if (!clazz) { \
805 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \
806 return JNI_FALSE; \
807 }
808
809 #define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
810 VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
811 if (!VAR) { \
812 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
813 return JNI_FALSE; \
814 }
815
816 #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
817 VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
818 if (!VAR) { \
819 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
820 return JNI_FALSE; \
821 }
822
823 #define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
824 VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
825 if (!VAR) { \
826 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
827 return JNI_FALSE; \
828 }
829
830 #define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \
831 VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \
832 if (!VAR) { \
833 __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \
834 return JNI_FALSE; \
835 }
836
registerNatives(JNIEnv * env)837 static int registerNatives(JNIEnv *env)
838 {
839 jclass clazz;
840 FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative");
841 m_applicationClass = static_cast<jclass>(env->NewGlobalRef(clazz));
842
843 if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
844 __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed");
845 return JNI_FALSE;
846 }
847
848 GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V");
849 GET_AND_CHECK_STATIC_METHOD(m_setSurfaceGeometryMethodID, m_applicationClass, "setSurfaceGeometry", "(IIIII)V");
850 GET_AND_CHECK_STATIC_METHOD(m_destroySurfaceMethodID, m_applicationClass, "destroySurface", "(I)V");
851
852 jmethodID methodID;
853 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;");
854 jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
855 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;");
856 jobject serviceObject = env->CallStaticObjectMethod(m_applicationClass, methodID);
857 GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;");
858 m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID));
859 clazz = env->GetObjectClass(m_classLoaderObject);
860 GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
861 if (serviceObject)
862 m_serviceObject = env->NewGlobalRef(serviceObject);
863
864 if (activityObject)
865 m_activityObject = env->NewGlobalRef(activityObject);
866
867 jobject object = activityObject ? activityObject : serviceObject;
868 if (object) {
869 FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
870 GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
871 m_assets = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
872 m_assetManager = AAssetManager_fromJava(env, m_assets);
873
874 GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
875 m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
876
877 FIND_AND_CHECK_CLASS("android/graphics/Bitmap");
878 m_bitmapClass = static_cast<jclass>(env->NewGlobalRef(clazz));
879 GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass
880 , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
881 FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config");
882 jfieldID fieldId;
883 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;");
884 m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
885 GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;");
886 m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId));
887
888 FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable");
889 m_bitmapDrawableClass = static_cast<jclass>(env->NewGlobalRef(clazz));
890 GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
891 m_bitmapDrawableClass,
892 "<init>",
893 "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V");
894 }
895
896 return JNI_TRUE;
897 }
898
899 QT_END_NAMESPACE
900
JNI_OnLoad(JavaVM * vm,void *)901 Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
902 {
903 static bool initialized = false;
904 if (initialized)
905 return JNI_VERSION_1_6;
906 initialized = true;
907
908 QT_USE_NAMESPACE
909 typedef union {
910 JNIEnv *nativeEnvironment;
911 void *venv;
912 } UnionJNIEnvToVoid;
913
914 UnionJNIEnvToVoid uenv;
915 uenv.venv = nullptr;
916 m_javaVM = nullptr;
917
918 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
919 __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed");
920 return -1;
921 }
922
923 JNIEnv *env = uenv.nativeEnvironment;
924 if (!registerNatives(env)
925 || !QtAndroidInput::registerNatives(env)
926 || !QtAndroidMenu::registerNatives(env)
927 || !QtAndroidAccessibility::registerNatives(env)
928 || !QtAndroidDialogHelpers::registerNatives(env)) {
929 __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
930 return -1;
931 }
932 QWindowSystemInterfacePrivate::TabletEvent::setPlatformSynthesizesMouse(false);
933
934 m_javaVM = vm;
935 // attach qt main thread data to this thread
936 QObject threadSetter;
937 if (threadSetter.thread())
938 threadSetter.thread()->setObjectName("QtMainLoopThread");
939 __android_log_print(ANDROID_LOG_INFO, "Qt", "qt started");
940 return JNI_VERSION_1_6;
941 }
942