/**************************************************************************** ** ** Copyright (C) 2014 BogDan Vatra ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or (at your option) the GNU General ** Public license version 3 or any later version approved by the KDE Free ** Qt Foundation. The licenses are as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-2.0.html and ** https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include "androidjnimain.h" #include "androidjniaccessibility.h" #include "androidjniinput.h" #include "androidjniclipboard.h" #include "androidjnimenu.h" #include "androiddeadlockprotector.h" #include "qandroidplatformdialoghelpers.h" #include "qandroidplatformintegration.h" #include "qandroidassetsfileenginehandler.h" #include #include #include "qandroideventdispatcher.h" #include #include #include #include #include #include Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin) QT_BEGIN_NAMESPACE static JavaVM *m_javaVM = nullptr; static jclass m_applicationClass = nullptr; static jobject m_classLoaderObject = nullptr; static jmethodID m_loadClassMethodID = nullptr; static AAssetManager *m_assetManager = nullptr; static jobject m_resourcesObj = nullptr; static jobject m_activityObject = nullptr; static jmethodID m_createSurfaceMethodID = nullptr; static jobject m_serviceObject = nullptr; static jmethodID m_setSurfaceGeometryMethodID = nullptr; static jmethodID m_destroySurfaceMethodID = nullptr; static bool m_activityActive = true; // defaults to true because when the platform plugin is // initialized, QtActivity::onResume() has already been called static jclass m_bitmapClass = nullptr; static jmethodID m_createBitmapMethodID = nullptr; static jobject m_ARGB_8888_BitmapConfigValue = nullptr; static jobject m_RGB_565_BitmapConfigValue = nullptr; static bool m_statusBarShowing = true; static jclass m_bitmapDrawableClass = nullptr; static jmethodID m_bitmapDrawableConstructorMethodID = nullptr; extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application static Main m_main = nullptr; static void *m_mainLibraryHnd = nullptr; static QList m_applicationParams; pthread_t m_qtAppThread = 0; static sem_t m_exitSemaphore, m_terminateSemaphore; struct SurfaceData { ~SurfaceData() { delete surface; } QJNIObjectPrivate *surface = nullptr; AndroidSurfaceClient *client = nullptr; }; QHash m_surfaces; static QMutex m_surfacesMutex; static int m_surfaceId = 1; static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr; static int m_desktopWidthPixels = 0; static int m_desktopHeightPixels = 0; static double m_scaledDensity = 0; static double m_density = 1.0; static volatile bool m_pauseApplication; static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr; static const char m_qtTag[] = "Qt"; static const char m_classErrorMsg[] = "Can't find class \"%s\""; static const char m_methodErrorMsg[] = "Can't find method \"%s%s\""; namespace QtAndroid { void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration) { m_surfacesMutex.lock(); m_androidPlatformIntegration = androidPlatformIntegration; m_surfacesMutex.unlock(); } QAndroidPlatformIntegration *androidPlatformIntegration() { QMutexLocker locker(&m_surfacesMutex); return m_androidPlatformIntegration; } QWindow *topLevelWindowAt(const QPoint &globalPos) { return m_androidPlatformIntegration ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos) : 0; } int desktopWidthPixels() { return m_desktopWidthPixels; } int desktopHeightPixels() { return m_desktopHeightPixels; } double scaledDensity() { return m_scaledDensity; } double pixelDensity() { return m_density; } JavaVM *javaVM() { return m_javaVM; } AAssetManager *assetManager() { return m_assetManager; } jclass applicationClass() { return m_applicationClass; } jobject activity() { return m_activityObject; } jobject service() { return m_serviceObject; } void showStatusBar() { if (m_statusBarShowing) return; QJNIObjectPrivate::callStaticMethod(m_applicationClass, "setFullScreen", "(Z)V", false); m_statusBarShowing = true; } void hideStatusBar() { if (!m_statusBarShowing) return; QJNIObjectPrivate::callStaticMethod(m_applicationClass, "setFullScreen", "(Z)V", true); m_statusBarShowing = false; } void setApplicationActive() { if (m_activityActive) QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationActive); } jobject createBitmap(QImage img, JNIEnv *env) { if (!m_bitmapClass) return 0; if (img.format() != QImage::Format_RGBA8888 && img.format() != QImage::Format_RGB16) img = img.convertToFormat(QImage::Format_RGBA8888); jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass, m_createBitmapMethodID, img.width(), img.height(), img.format() == QImage::Format_RGBA8888 ? m_ARGB_8888_BitmapConfigValue : m_RGB_565_BitmapConfigValue); if (!bitmap) return 0; AndroidBitmapInfo info; if (AndroidBitmap_getInfo(env, bitmap, &info) < 0) { env->DeleteLocalRef(bitmap); return 0; } void *pixels; if (AndroidBitmap_lockPixels(env, bitmap, &pixels) < 0) { env->DeleteLocalRef(bitmap); return 0; } if (info.stride == uint(img.bytesPerLine()) && info.width == uint(img.width()) && info.height == uint(img.height())) { memcpy(pixels, img.constBits(), info.stride * info.height); } else { uchar *bmpPtr = static_cast(pixels); const unsigned width = qMin(info.width, (uint)img.width()); //should be the same const unsigned height = qMin(info.height, (uint)img.height()); //should be the same for (unsigned y = 0; y < height; y++, bmpPtr += info.stride) memcpy(bmpPtr, img.constScanLine(y), width); } AndroidBitmap_unlockPixels(env, bitmap); return bitmap; } jobject createBitmap(int width, int height, QImage::Format format, JNIEnv *env) { if (format != QImage::Format_RGBA8888 && format != QImage::Format_RGB16) return 0; return env->CallStaticObjectMethod(m_bitmapClass, m_createBitmapMethodID, width, height, format == QImage::Format_RGB16 ? m_RGB_565_BitmapConfigValue : m_ARGB_8888_BitmapConfigValue); } jobject createBitmapDrawable(jobject bitmap, JNIEnv *env) { if (!bitmap || !m_bitmapDrawableClass || !m_resourcesObj) return 0; return env->NewObject(m_bitmapDrawableClass, m_bitmapDrawableConstructorMethodID, m_resourcesObj, bitmap); } const char *classErrorMsgFmt() { return m_classErrorMsg; } const char *methodErrorMsgFmt() { return m_methodErrorMsg; } const char *qtTagText() { return m_qtTag; } QString deviceName() { QString manufacturer = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString(); QString model = QJNIObjectPrivate::getStaticObjectField("android/os/Build", "MODEL", "Ljava/lang/String;").toString(); return manufacturer + QLatin1Char(' ') + model; } int createSurface(AndroidSurfaceClient *client, const QRect &geometry, bool onTop, int imageDepth) { QJNIEnvironmentPrivate env; if (!env) return -1; m_surfacesMutex.lock(); int surfaceId = m_surfaceId++; m_surfaces[surfaceId] = client; m_surfacesMutex.unlock(); jint x = 0, y = 0, w = -1, h = -1; if (!geometry.isNull()) { x = geometry.x(); y = geometry.y(); w = std::max(geometry.width(), 1); h = std::max(geometry.height(), 1); } env->CallStaticVoidMethod(m_applicationClass, m_createSurfaceMethodID, surfaceId, jboolean(onTop), x, y, w, h, imageDepth); return surfaceId; } int insertNativeView(jobject view, const QRect &geometry) { m_surfacesMutex.lock(); const int surfaceId = m_surfaceId++; m_surfaces[surfaceId] = nullptr; // dummy m_surfacesMutex.unlock(); jint x = 0, y = 0, w = -1, h = -1; if (!geometry.isNull()) geometry.getRect(&x, &y, &w, &h); QJNIObjectPrivate::callStaticMethod(m_applicationClass, "insertNativeView", "(ILandroid/view/View;IIII)V", surfaceId, view, x, y, qMax(w, 1), qMax(h, 1)); return surfaceId; } void setViewVisibility(jobject view, bool visible) { QJNIObjectPrivate::callStaticMethod(m_applicationClass, "setViewVisibility", "(Landroid/view/View;Z)V", view, visible); } void setSurfaceGeometry(int surfaceId, const QRect &geometry) { if (surfaceId == -1) return; QJNIEnvironmentPrivate env; if (!env) return; jint x = 0, y = 0, w = -1, h = -1; if (!geometry.isNull()) { x = geometry.x(); y = geometry.y(); w = geometry.width(); h = geometry.height(); } env->CallStaticVoidMethod(m_applicationClass, m_setSurfaceGeometryMethodID, surfaceId, x, y, w, h); } void destroySurface(int surfaceId) { if (surfaceId == -1) return; QMutexLocker lock(&m_surfacesMutex); const auto &it = m_surfaces.find(surfaceId); if (it != m_surfaces.end()) m_surfaces.remove(surfaceId); QJNIEnvironmentPrivate env; if (!env) return; env->CallStaticVoidMethod(m_applicationClass, m_destroySurfaceMethodID, surfaceId); } void bringChildToFront(int surfaceId) { if (surfaceId == -1) return; QJNIObjectPrivate::callStaticMethod(m_applicationClass, "bringChildToFront", "(I)V", surfaceId); } void bringChildToBack(int surfaceId) { if (surfaceId == -1) return; QJNIObjectPrivate::callStaticMethod(m_applicationClass, "bringChildToBack", "(I)V", surfaceId); } bool blockEventLoopsWhenSuspended() { static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED"); return block; } } // namespace QtAndroid static jboolean startQtAndroidPlugin(JNIEnv* /*env*/, jobject /*object*//*, jobject applicationAssetManager*/) { m_androidPlatformIntegration = nullptr; m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler(); return true; } static void *startMainMethod(void */*data*/) { QVarLengthArray params(m_applicationParams.size()); for (int i = 0; i < m_applicationParams.size(); i++) params[i] = static_cast(m_applicationParams[i].constData()); int ret = m_main(m_applicationParams.length(), const_cast(params.data())); if (m_mainLibraryHnd) { int res = dlclose(m_mainLibraryHnd); if (res < 0) qWarning() << "dlclose failed:" << dlerror(); } if (m_applicationClass) QJNIObjectPrivate::callStaticMethod(m_applicationClass, "quitApp", "()V"); // All attached threads should be detached before returning from this function. JavaVM *vm = QtAndroidPrivate::javaVM(); if (vm != 0) vm->DetachCurrentThread(); sem_post(&m_terminateSemaphore); sem_wait(&m_exitSemaphore); sem_destroy(&m_exitSemaphore); // We must call exit() to ensure that all global objects will be destructed exit(ret); return 0; } static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString) { m_mainLibraryHnd = nullptr; { // Set env. vars const char *nativeString = env->GetStringUTFChars(environmentString, 0); const QList envVars = QByteArray(nativeString).split('\t'); env->ReleaseStringUTFChars(environmentString, nativeString); foreach (const QByteArray &envVar, envVars) { const QList envVarPair = envVar.split('='); if (envVarPair.size() == 2 && ::setenv(envVarPair[0], envVarPair[1], 1) != 0) qWarning() << "Can't set environment" << envVarPair; } } const char *nativeString = env->GetStringUTFChars(paramsString, 0); QByteArray string = nativeString; env->ReleaseStringUTFChars(paramsString, nativeString); m_applicationParams=string.split('\t'); // Go home QDir::setCurrent(QDir::homePath()); //look for main() if (m_applicationParams.length()) { // Obtain a handle to the main library (the library that contains the main() function). // This library should already be loaded, and calling dlopen() will just return a reference to it. m_mainLibraryHnd = dlopen(m_applicationParams.first().data(), 0); if (Q_UNLIKELY(!m_mainLibraryHnd)) { qCritical() << "dlopen failed:" << dlerror(); return false; } m_main = (Main)dlsym(m_mainLibraryHnd, "main"); } else { qWarning() << "No main library was specified; searching entire process (this is slow!)"; m_main = (Main)dlsym(RTLD_DEFAULT, "main"); } if (Q_UNLIKELY(!m_main)) { qCritical() << "dlsym failed:" << dlerror() << endl << "Could not find main method"; return false; } if (sem_init(&m_exitSemaphore, 0, 0) == -1) return false; if (sem_init(&m_terminateSemaphore, 0, 0) == -1) return false; return pthread_create(&m_qtAppThread, nullptr, startMainMethod, nullptr) == 0; } static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/) { Q_UNUSED(env); m_androidPlatformIntegration = nullptr; delete m_androidAssetsFileEngineHandler; m_androidAssetsFileEngineHandler = nullptr; } static void terminateQt(JNIEnv *env, jclass /*clazz*/) { sem_wait(&m_terminateSemaphore); sem_destroy(&m_terminateSemaphore); env->DeleteGlobalRef(m_applicationClass); env->DeleteGlobalRef(m_classLoaderObject); if (m_resourcesObj) env->DeleteGlobalRef(m_resourcesObj); if (m_activityObject) env->DeleteGlobalRef(m_activityObject); if (m_serviceObject) env->DeleteGlobalRef(m_serviceObject); if (m_bitmapClass) env->DeleteGlobalRef(m_bitmapClass); if (m_ARGB_8888_BitmapConfigValue) env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue); if (m_RGB_565_BitmapConfigValue) env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue); if (m_bitmapDrawableClass) env->DeleteGlobalRef(m_bitmapDrawableClass); m_androidPlatformIntegration = nullptr; delete m_androidAssetsFileEngineHandler; m_androidAssetsFileEngineHandler = nullptr; sem_post(&m_exitSemaphore); pthread_join(m_qtAppThread, nullptr); } static void setSurface(JNIEnv *env, jobject /*thiz*/, jint id, jobject jSurface, jint w, jint h) { QMutexLocker lock(&m_surfacesMutex); const auto &it = m_surfaces.find(id); if (it.value() == nullptr) // This should never happen... return; if (it == m_surfaces.end()) { qWarning()<<"Can't find surface" << id; return; } it.value()->surfaceChanged(env, jSurface, w, h); } static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, jint widthPixels, jint heightPixels, jint desktopWidthPixels, jint desktopHeightPixels, jdouble xdpi, jdouble ydpi, jdouble scaledDensity, jdouble density) { // Android does not give us the correct screen size for immersive mode, but // the surface does have the right size widthPixels = qMax(widthPixels, desktopWidthPixels); heightPixels = qMax(heightPixels, desktopHeightPixels); m_desktopWidthPixels = desktopWidthPixels; m_desktopHeightPixels = desktopHeightPixels; m_scaledDensity = scaledDensity; m_density = density; if (!m_androidPlatformIntegration) { QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels, desktopHeightPixels, qRound(double(widthPixels) / xdpi * 25.4), qRound(double(heightPixels) / ydpi * 25.4), widthPixels, heightPixels); } else { m_androidPlatformIntegration->setDisplayMetrics(qRound(double(widthPixels) / xdpi * 25.4), qRound(double(heightPixels) / ydpi * 25.4)); m_androidPlatformIntegration->setScreenSize(widthPixels, heightPixels); m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels); } } static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) { if (!m_androidPlatformIntegration) return; if (QGuiApplication::instance() != nullptr) { foreach (QWindow *w, QGuiApplication::topLevelWindows()) { QRect availableGeometry = w->screen()->availableGeometry(); if (w->geometry().width() > 0 && w->geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) QWindowSystemInterface::handleExposeEvent(w, QRegion(QRect(QPoint(), w->geometry().size()))); } } QAndroidPlatformScreen *screen = static_cast(m_androidPlatformIntegration->screen()); if (screen->rasterSurfaces()) QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); } static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state) { m_activityActive = (state == Qt::ApplicationActive); if (!m_main || !m_androidPlatformIntegration || !QGuiApplicationPrivate::platformIntegration()) { QAndroidPlatformIntegration::setDefaultApplicationState(Qt::ApplicationState(state)); return; } if (state == Qt::ApplicationActive) QtAndroidPrivate::handleResume(); else if (state == Qt::ApplicationInactive) QtAndroidPrivate::handlePause(); if (state <= Qt::ApplicationInactive) { // NOTE: sometimes we will receive two consecutive suspended notifications, // In the second suspended notification, QWindowSystemInterface::flushWindowSystemEvents() // will deadlock since the dispatcher has been stopped in the first suspended notification. // To avoid the deadlock we simply return if we found the event dispatcher has been stopped. if (QAndroidEventDispatcherStopper::instance()->stopped()) return; // Don't send timers and sockets events anymore if we are going to hide all windows QAndroidEventDispatcherStopper::instance()->goingToStop(true); QCoreApplication::processEvents(); QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); { AndroidDeadlockProtector protector; if (protector.acquire()) QWindowSystemInterface::flushWindowSystemEvents(); } if (state == Qt::ApplicationSuspended) QAndroidEventDispatcherStopper::instance()->stopAll(); } else { QAndroidEventDispatcherStopper::instance()->startAll(); QWindowSystemInterface::handleApplicationStateChanged(Qt::ApplicationState(state)); QAndroidEventDispatcherStopper::instance()->goingToStop(false); } } static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newRotation, jint nativeOrientation) { // Array of orientations rotated in 90 degree increments, counterclockwise // (same direction as Android measures angles) static const Qt::ScreenOrientation orientations[] = { Qt::PortraitOrientation, Qt::LandscapeOrientation, Qt::InvertedPortraitOrientation, Qt::InvertedLandscapeOrientation }; // The Android API defines the following constants: // ROTATION_0 : 0 // ROTATION_90 : 1 // ROTATION_180 : 2 // ROTATION_270 : 3 // ORIENTATION_PORTRAIT : 1 // ORIENTATION_LANDSCAPE : 2 // and newRotation is how much the current orientation is rotated relative to nativeOrientation // which means that we can be really clever here :) Qt::ScreenOrientation screenOrientation = orientations[(nativeOrientation - 1 + newRotation) % 4]; Qt::ScreenOrientation native = orientations[nativeOrientation - 1]; QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native); if (m_androidPlatformIntegration) { QPlatformScreen *screen = m_androidPlatformIntegration->screen(); QWindowSystemInterface::handleScreenOrientationChange(screen->screen(), screenOrientation); } } static void onActivityResult(JNIEnv */*env*/, jclass /*cls*/, jint requestCode, jint resultCode, jobject data) { QtAndroidPrivate::handleActivityResult(requestCode, resultCode, data); } static void onNewIntent(JNIEnv *env, jclass /*cls*/, jobject data) { QtAndroidPrivate::handleNewIntent(env, data); } static JNINativeMethod methods[] = { {"startQtAndroidPlugin", "()Z", (void *)startQtAndroidPlugin}, {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication}, {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin}, {"terminateQt", "()V", (void *)terminateQt}, {"setDisplayMetrics", "(IIIIDDDD)V", (void *)setDisplayMetrics}, {"setSurface", "(ILjava/lang/Object;II)V", (void *)setSurface}, {"updateWindow", "()V", (void *)updateWindow}, {"updateApplicationState", "(I)V", (void *)updateApplicationState}, {"handleOrientationChanged", "(II)V", (void *)handleOrientationChanged}, {"onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult}, {"onNewIntent", "(Landroid/content/Intent;)V", (void *)onNewIntent} }; #define FIND_AND_CHECK_CLASS(CLASS_NAME) \ clazz = env->FindClass(CLASS_NAME); \ if (!clazz) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_classErrorMsg, CLASS_NAME); \ return JNI_FALSE; \ } #define GET_AND_CHECK_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ VAR = env->GetMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ return JNI_FALSE; \ } #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ return JNI_FALSE; \ } #define GET_AND_CHECK_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ VAR = env->GetFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ return JNI_FALSE; \ } #define GET_AND_CHECK_STATIC_FIELD(VAR, CLASS, FIELD_NAME, FIELD_SIGNATURE) \ VAR = env->GetStaticFieldID(CLASS, FIELD_NAME, FIELD_SIGNATURE); \ if (!VAR) { \ __android_log_print(ANDROID_LOG_FATAL, m_qtTag, m_methodErrorMsg, FIELD_NAME, FIELD_SIGNATURE); \ return JNI_FALSE; \ } static int registerNatives(JNIEnv *env) { jclass clazz; FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/QtNative"); m_applicationClass = static_cast(env->NewGlobalRef(clazz)); if (env->RegisterNatives(m_applicationClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); return JNI_FALSE; } GET_AND_CHECK_STATIC_METHOD(m_createSurfaceMethodID, m_applicationClass, "createSurface", "(IZIIIII)V"); GET_AND_CHECK_STATIC_METHOD(m_setSurfaceGeometryMethodID, m_applicationClass, "setSurfaceGeometry", "(IIIII)V"); GET_AND_CHECK_STATIC_METHOD(m_destroySurfaceMethodID, m_applicationClass, "destroySurface", "(I)V"); jmethodID methodID; GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "service", "()Landroid/app/Service;"); jobject serviceObject = env->CallStaticObjectMethod(m_applicationClass, methodID); GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "classLoader", "()Ljava/lang/ClassLoader;"); m_classLoaderObject = env->NewGlobalRef(env->CallStaticObjectMethod(m_applicationClass, methodID)); clazz = env->GetObjectClass(m_classLoaderObject); GET_AND_CHECK_METHOD(m_loadClassMethodID, clazz, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); if (serviceObject) m_serviceObject = env->NewGlobalRef(serviceObject); if (activityObject) m_activityObject = env->NewGlobalRef(activityObject); jobject object = activityObject ? activityObject : serviceObject; if (object) { FIND_AND_CHECK_CLASS("android/content/ContextWrapper"); GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;"); m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(object, methodID)); GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID)); FIND_AND_CHECK_CLASS("android/graphics/Bitmap"); m_bitmapClass = static_cast(env->NewGlobalRef(clazz)); GET_AND_CHECK_STATIC_METHOD(m_createBitmapMethodID, m_bitmapClass , "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;"); FIND_AND_CHECK_CLASS("android/graphics/Bitmap$Config"); jfieldID fieldId; GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "ARGB_8888", "Landroid/graphics/Bitmap$Config;"); m_ARGB_8888_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); GET_AND_CHECK_STATIC_FIELD(fieldId, clazz, "RGB_565", "Landroid/graphics/Bitmap$Config;"); m_RGB_565_BitmapConfigValue = env->NewGlobalRef(env->GetStaticObjectField(clazz, fieldId)); FIND_AND_CHECK_CLASS("android/graphics/drawable/BitmapDrawable"); m_bitmapDrawableClass = static_cast(env->NewGlobalRef(clazz)); GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID, m_bitmapDrawableClass, "", "(Landroid/content/res/Resources;Landroid/graphics/Bitmap;)V"); } return JNI_TRUE; } QT_END_NAMESPACE Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) { QT_USE_NAMESPACE typedef union { JNIEnv *nativeEnvironment; void *venv; } UnionJNIEnvToVoid; __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start"); UnionJNIEnvToVoid uenv; uenv.venv = nullptr; m_javaVM = nullptr; if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "GetEnv failed"); return -1; } JNIEnv *env = uenv.nativeEnvironment; if (!registerNatives(env) || !QtAndroidInput::registerNatives(env) || !QtAndroidMenu::registerNatives(env) || !QtAndroidAccessibility::registerNatives(env) || !QtAndroidDialogHelpers::registerNatives(env)) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } m_javaVM = vm; return JNI_VERSION_1_4; }