From 97fcf3bc987a18f32233fea550eb4a5a22e2b822 Mon Sep 17 00:00:00 2001 From: Paul Olav Tvete Date: Mon, 4 Mar 2013 10:16:42 +0100 Subject: Introducing the Qt Android port MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the Necessitas project by Bogdan Vatra. Contributors to the Qt5 project: BogDan Vatra Eskil Abrahamsen Blomfeldt hjk Oswald Buddenhagen Paul Olav Tvete Robin Burchell Samuel Rødal Yoann Lopes The full history of the Qt5 port can be found in refs/old-heads/android, SHA-1 249ca9ca2c7d876b91b31df9434dde47f9065d0d Change-Id: Iff1a7b2dbb707c986f2639e65e39ed8f22430120 Reviewed-by: Oswald Buddenhagen Reviewed-by: Lars Knoll --- .../platforms/android/src/androidjnimain.cpp | 839 +++++++++++++++++++++ 1 file changed, 839 insertions(+) create mode 100644 src/plugins/platforms/android/src/androidjnimain.cpp (limited to 'src/plugins/platforms/android/src/androidjnimain.cpp') diff --git a/src/plugins/platforms/android/src/androidjnimain.cpp b/src/plugins/platforms/android/src/androidjnimain.cpp new file mode 100644 index 0000000000..2a4c48df3c --- /dev/null +++ b/src/plugins/platforms/android/src/androidjnimain.cpp @@ -0,0 +1,839 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2012 BogDan Vatra +** Contact: http://www.qt-project.org/legal +** +** 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 Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "androidjnimain.h" +#include "androidjniinput.h" +#include "androidjniclipboard.h" +#include "androidjnimenu.h" +#include "qandroidplatformintegration.h" +#include + +#include + +#include +#include +#include "qandroidassetsfileenginehandler.h" +#include + +#include + +#ifdef ANDROID_PLUGIN_OPENGL +# include "qandroidopenglplatformwindow.h" +#endif + +#if __ANDROID_API__ > 8 +# include +#endif + +static jmethodID m_redrawSurfaceMethodID = 0; + +Q_IMPORT_PLUGIN(QAndroidPlatformIntegrationPlugin) + +static JavaVM *m_javaVM = NULL; +static jclass m_applicationClass = NULL; +static jobject m_classLoaderObject = NULL; +static jmethodID m_loadClassMethodID = NULL; +static AAssetManager *m_assetManager = NULL; +static jobject m_resourcesObj; +static jobject m_activityObject = NULL; + +static jclass m_bitmapClass = 0; +static jmethodID m_createBitmapMethodID = 0; +static jobject m_ARGB_8888_BitmapConfigValue = 0; +static jobject m_RGB_565_BitmapConfigValue = 0; + +static jclass m_bitmapDrawableClass = 0; +static jmethodID m_bitmapDrawableConstructorMethodID = 0; + +extern "C" typedef int (*Main)(int, char **); //use the standard main method to start the application +static Main m_main = NULL; +static void *m_mainLibraryHnd = NULL; +static QList m_applicationParams; + +#ifndef ANDROID_PLUGIN_OPENGL +static jobject m_surface = NULL; +#else +static EGLNativeWindowType m_nativeWindow = 0; +static QSemaphore m_waitForWindowSemaphore; +static bool m_waitForWindow = false; + +static jfieldID m_surfaceFieldID = 0; +#endif + + +static QSemaphore m_quitAppSemaphore; +static QMutex m_surfaceMutex(QMutex::Recursive); +static QSemaphore m_pauseApplicationSemaphore; +static QMutex m_pauseApplicationMutex; + +static QAndroidPlatformIntegration *m_androidPlatformIntegration = 0; + +static int m_desktopWidthPixels = 0; +static int m_desktopHeightPixels = 0; + +static volatile bool m_pauseApplication; + +static jmethodID m_setFullScreenMethodID = 0; + +static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = 0; + + + +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\""; + +static inline void checkPauseApplication() +{ + m_pauseApplicationMutex.lock(); + if (m_pauseApplication) { + m_pauseApplicationMutex.unlock(); + m_pauseApplicationSemaphore.acquire(); // wait until surface is created + + m_pauseApplicationMutex.lock(); + m_pauseApplication = false; + m_pauseApplicationMutex.unlock(); + + //FIXME +// QWindowSystemInterface::handleScreenAvailableGeometryChange(0); +// QWindowSystemInterface::handleScreenGeometryChange(0); + } else { + m_pauseApplicationMutex.unlock(); + } +} + +namespace QtAndroid +{ +#ifndef ANDROID_PLUGIN_OPENGL + void flushImage(const QPoint &pos, const QImage &image, const QRect &destinationRect) + { + checkPauseApplication(); + QMutexLocker locker(&m_surfaceMutex); + if (!m_surface) + return; + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + int bpp = 2; + AndroidBitmapInfo info; + int ret; + + if ((ret = AndroidBitmap_getInfo(env.jniEnv, m_surface, &info)) < 0) { + qWarning() << "AndroidBitmap_getInfo() failed ! error=" << ret; + m_javaVM->DetachCurrentThread(); + return; + } + + if (info.format != ANDROID_BITMAP_FORMAT_RGB_565) { + qWarning() << "Bitmap format is not RGB_565!"; + m_javaVM->DetachCurrentThread(); + return; + } + + void *pixels; + unsigned char *screenBits; + if ((ret = AndroidBitmap_lockPixels(env.jniEnv, m_surface, &pixels)) < 0) { + qWarning() << "AndroidBitmap_lockPixels() failed! error=" << ret; + m_javaVM->DetachCurrentThread(); + return; + } + + screenBits = static_cast(pixels); + int sbpl = info.stride; + int swidth = info.width; + int sheight = info.height; + + unsigned sposx = pos.x() + destinationRect.x(); + unsigned sposy = pos.y() + destinationRect.y(); + + screenBits += sposy * sbpl; + + unsigned ibpl = image.bytesPerLine(); + unsigned iposx = destinationRect.x(); + unsigned iposy = destinationRect.y(); + + const unsigned char *imageBits = static_cast(image.bits()); + imageBits += iposy * ibpl; + + unsigned width = swidth - sposx < unsigned(destinationRect.width()) + ? (swidth-sposx) + : destinationRect.width(); + unsigned height = sheight - sposy < unsigned(destinationRect.height()) + ? (sheight - sposy) + : destinationRect.height(); + + for (unsigned y = 0; y < height; y++) { + memcpy(screenBits + y*sbpl + sposx*bpp, + imageBits + y*ibpl + iposx*bpp, + width*bpp); + } + AndroidBitmap_unlockPixels(env.jniEnv, m_surface); + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, + m_redrawSurfaceMethodID, + jint(destinationRect.left()), + jint(destinationRect.top()), + jint(destinationRect.right() + 1), + jint(destinationRect.bottom() + 1)); +#warning FIXME dirty hack, figure out why it needs to add 1 to right and bottom !!!! + } + +#else // for #ifndef ANDROID_PLUGIN_OPENGL + EGLNativeWindowType nativeWindow(bool waitForWindow) + { + m_surfaceMutex.lock(); + if (!m_nativeWindow && waitForWindow) { + m_waitForWindow = true; + m_surfaceMutex.unlock(); + m_waitForWindowSemaphore.acquire(); + m_waitForWindow = false; + return m_nativeWindow; + } + m_surfaceMutex.unlock(); + return m_nativeWindow; + } + + QSize nativeWindowSize() + { + if (m_nativeWindow == 0) + return QAndroidPlatformIntegration::defaultDesktopSize(); + + int width = ANativeWindow_getWidth(m_nativeWindow); + int height = ANativeWindow_getHeight(m_nativeWindow); + + return QSize(width, height); + } +#endif + + void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration) + { + m_surfaceMutex.lock(); + m_androidPlatformIntegration = androidPlatformIntegration; + m_surfaceMutex.unlock(); + } + + void setFullScreen(QWidget *widget) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + bool fullScreen = widget->isFullScreen(); + if (!fullScreen) { + foreach (QWidget *w, qApp->topLevelWidgets()) { + fullScreen |= w->isFullScreen(); + if (fullScreen) + break; + } + } + + env.jniEnv->CallStaticVoidMethod(m_applicationClass, m_setFullScreenMethodID, fullScreen); + } + + QWindow *topLevelWindowAt(const QPoint &globalPos) + { + return m_androidPlatformIntegration + ? m_androidPlatformIntegration->screen()->topLevelAt(globalPos) + : 0; + } + + int desktopWidthPixels() + { + return m_desktopWidthPixels; + } + + int desktopHeightPixels() + { + return m_desktopHeightPixels; + } + + JavaVM *javaVM() + { + return m_javaVM; + } + + jclass findClass(const QString &className, JNIEnv *env) + { + return static_cast(env->CallObjectMethod(m_classLoaderObject, + m_loadClassMethodID, + env->NewString(reinterpret_cast(className.constData()), + jsize(className.length())))); + } + + AAssetManager *assetManager() + { + return m_assetManager; + } + + jclass applicationClass() + { + return m_applicationClass; + } + + jobject activity() + { + return m_activityObject; + } + + jobject createBitmap(QImage img, JNIEnv *env) + { + if (img.format() != QImage::Format_ARGB32 && img.format() != QImage::Format_RGB16) + img = img.convertToFormat(QImage::Format_ARGB32); + + jobject bitmap = env->CallStaticObjectMethod(m_bitmapClass, + m_createBitmapMethodID, + img.width(), + img.height(), + img.format() == QImage::Format_ARGB32 + ? 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 createBitmapDrawable(jobject bitmap, JNIEnv *env) + { + if (!bitmap) + 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; + } +} + +static jboolean startQtAndroidPlugin(JNIEnv* /*env*/, jobject /*object*//*, jobject applicationAssetManager*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + m_surface = 0; +#else + m_nativeWindow = 0; + m_waitForWindow = false; +#endif + + m_androidPlatformIntegration = 0; + m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler(); + +#ifdef ANDROID_PLUGIN_OPENGL + return true; +#else + return false; +#endif +} + +static void *startMainMethod(void */*data*/) +{ + char const **params; + params = static_cast(malloc(m_applicationParams.length() * sizeof(char *))); + 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)); + + free(params); + Q_UNUSED(ret); + + if (m_mainLibraryHnd) { + int res = dlclose(m_mainLibraryHnd); + if (res < 0) + qWarning() << "dlclose failed:" << dlerror(); + } + + QtAndroid::AttachedJNIEnv env; + if (!env.jniEnv) + return 0; + + if (m_applicationClass) { + jmethodID quitApp = env.jniEnv->GetStaticMethodID(m_applicationClass, "quitApp", "()V"); + env.jniEnv->CallStaticVoidMethod(m_applicationClass, quitApp); + } + + return 0; +} + +static jboolean startQtApplication(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString) +{ + m_mainLibraryHnd = NULL; + const char *nativeString = env->GetStringUTFChars(environmentString, 0); + QByteArray string = nativeString; + env->ReleaseStringUTFChars(environmentString, nativeString); + m_applicationParams=string.split('\t'); + foreach (string, m_applicationParams) { + if (putenv(string.constData())) + qWarning() << "Can't set environment" << string; + } + + nativeString = env->GetStringUTFChars(paramsString, 0); + 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 (m_mainLibraryHnd == NULL) { + 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 (!m_main) { + qCritical() << "dlsym failed:" << dlerror(); + qCritical() << "Could not find main method"; + return false; + } + + pthread_t appThread; + return pthread_create(&appThread, NULL, startMainMethod, NULL) == 0; +} + +static void pauseQtApp(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); + m_pauseApplicationMutex.lock(); + + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->pauseApp(); + m_pauseApplication = true; + + m_pauseApplicationMutex.unlock(); + m_surfaceMutex.unlock(); +} + +static void resumeQtApp(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); + m_pauseApplicationMutex.lock(); + if (m_androidPlatformIntegration) + m_androidPlatformIntegration->resumeApp(); + + if (m_pauseApplication) + m_pauseApplicationSemaphore.release(); + + m_pauseApplicationMutex.unlock(); + m_surfaceMutex.unlock(); +} + +static void quitQtAndroidPlugin(JNIEnv *env, jclass /*clazz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) { + env->DeleteGlobalRef(m_surface); + m_surface = 0; + } +#else + Q_UNUSED(env); +#endif + + m_androidPlatformIntegration = 0; + delete m_androidAssetsFileEngineHandler; +} + +static void terminateQt(JNIEnv *env, jclass /*clazz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) + env->DeleteGlobalRef(m_surface); +#endif + env->DeleteGlobalRef(m_applicationClass); + env->DeleteGlobalRef(m_classLoaderObject); + env->DeleteGlobalRef(m_resourcesObj); + env->DeleteGlobalRef(m_activityObject); + env->DeleteGlobalRef(m_bitmapClass); + env->DeleteGlobalRef(m_ARGB_8888_BitmapConfigValue); + env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue); + env->DeleteGlobalRef(m_bitmapDrawableClass); +} + +#ifdef ANDROID_PLUGIN_OPENGL +#if __ANDROID_API__ < 9 +struct FakeNativeWindow +{ + long long dummyNativeWindow;// force 64 bits alignment +}; + +class FakeSurface: public FakeNativeWindow +{ +public: + virtual void FakeSurfaceMethod() + { + fakeSurface = 0; + } + + int fakeSurface; +}; + +EGLNativeWindowType ANativeWindow_fromSurface(JNIEnv *env, jobject jSurface) +{ + FakeSurface *surface = static_cast(env->GetIntField(jSurface, m_surfaceFieldID)); + return static_cast(static_cast(surface)); +} +#endif // __ANDROID_API__ < 9 +#endif // ANDROID_PLUGIN_OPENGL + +static void setSurface(JNIEnv *env, jobject /*thiz*/, jobject jSurface) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) + env->DeleteGlobalRef(m_surface); + m_surface = env->NewGlobalRef(jSurface); +#else + m_surfaceMutex.lock(); + EGLNativeWindowType nativeWindow = ANativeWindow_fromSurface(env, jSurface); + bool sameNativeWindow = (nativeWindow != 0 && nativeWindow == m_nativeWindow); + + m_nativeWindow = nativeWindow; + if (m_waitForWindow) + m_waitForWindowSemaphore.release(); + if (m_androidPlatformIntegration && !sameNativeWindow) { + m_surfaceMutex.unlock(); + m_androidPlatformIntegration->surfaceChanged(); + } else if (m_androidPlatformIntegration && sameNativeWindow) { + QAndroidOpenGLPlatformWindow *window = m_androidPlatformIntegration->primaryWindow(); + QPlatformScreen *screen = m_androidPlatformIntegration->screen(); + QSize size = QtAndroid::nativeWindowSize(); + + QRect geometry(QPoint(0, 0), size); + QWindowSystemInterface::handleScreenAvailableGeometryChange(screen->screen(), geometry); + QWindowSystemInterface::handleScreenGeometryChange(screen->screen(), geometry); + + if (window != 0) { + window->lock(); + window->scheduleResize(size); + + QWindowSystemInterface::handleExposeEvent(window->window(), + QRegion(window->window()->geometry())); + window->unlock(); + } + + m_surfaceMutex.unlock(); + } else { + m_surfaceMutex.unlock(); + } +#endif // for #ifndef ANDROID_PLUGIN_OPENGL +} + +static void destroySurface(JNIEnv *env, jobject /*thiz*/) +{ +#ifndef ANDROID_PLUGIN_OPENGL + if (m_surface) { + env->DeleteGlobalRef(m_surface); + m_surface = 0; + } +#else + Q_UNUSED(env); + m_nativeWindow = 0; + if (m_androidPlatformIntegration != 0) + m_androidPlatformIntegration->invalidateNativeSurface(); +#endif +} + +static void setDisplayMetrics(JNIEnv */*env*/, jclass /*clazz*/, + jint /*widthPixels*/, jint /*heightPixels*/, + jint desktopWidthPixels, jint desktopHeightPixels, + jdouble xdpi, jdouble ydpi) +{ + m_desktopWidthPixels = desktopWidthPixels; + m_desktopHeightPixels = desktopHeightPixels; + + if (!m_androidPlatformIntegration) { + QAndroidPlatformIntegration::setDefaultDisplayMetrics(desktopWidthPixels,desktopHeightPixels, + qRound(double(desktopWidthPixels) / xdpi * 25.4), + qRound(double(desktopHeightPixels) / ydpi * 25.4)); + } else { + m_androidPlatformIntegration->setDisplayMetrics(qRound(double(desktopWidthPixels) / xdpi * 25.4), + qRound(double(desktopHeightPixels) / ydpi * 25.4)); + m_androidPlatformIntegration->setDesktopSize(desktopWidthPixels, desktopHeightPixels); + } +} + +static void lockSurface(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.lock(); +} + +static void unlockSurface(JNIEnv */*env*/, jobject /*thiz*/) +{ + m_surfaceMutex.unlock(); +} + +static void updateWindow(JNIEnv */*env*/, jobject /*thiz*/) +{ + if (!m_androidPlatformIntegration) + return; + + if (qApp != 0) { + foreach (QWidget *w, qApp->topLevelWidgets()) + w->update(); + } + +#ifndef ANDROID_PLUGIN_OPENGL + QAndroidPlatformScreen *screen = static_cast(m_androidPlatformIntegration->screen()); + QMetaObject::invokeMethod(screen, "setDirty", Qt::QueuedConnection, Q_ARG(QRect,screen->geometry())); +#else + qWarning("updateWindow: Dirty screen not implemented yet on OpenGL"); +#endif +} + +static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint newOrientation) +{ + if (m_androidPlatformIntegration == 0) + return; + + Qt::ScreenOrientation screenOrientation = newOrientation == 1 + ? Qt::PortraitOrientation + : Qt::LandscapeOrientation; + QPlatformScreen *screen = m_androidPlatformIntegration->screen(); + QWindowSystemInterface::handleScreenOrientationChange(screen->screen(), + screenOrientation); +} + +static JNINativeMethod methods[] = { + {"startQtAndroidPlugin", "()Z", (void *)startQtAndroidPlugin}, + {"startQtApplication", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)startQtApplication}, + {"pauseQtApp", "()V", (void *)pauseQtApp}, + {"resumeQtApp", "()V", (void *)resumeQtApp}, + {"quitQtAndroidPlugin", "()V", (void *)quitQtAndroidPlugin}, + {"terminateQt", "()V", (void *)terminateQt}, + {"setDisplayMetrics", "(IIIIDD)V", (void *)setDisplayMetrics}, + {"setSurface", "(Ljava/lang/Object;)V", (void *)setSurface}, + {"destroySurface", "()V", (void *)destroySurface}, + {"lockSurface", "()V", (void *)lockSurface}, + {"unlockSurface", "()V", (void *)unlockSurface}, + {"updateWindow", "()V", (void *)updateWindow}, + {"handleOrientationChanged", "(I)V", (void *)handleOrientationChanged} +}; + +#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_redrawSurfaceMethodID, m_applicationClass, "redrawSurface", "(IIII)V"); + GET_AND_CHECK_STATIC_METHOD(m_setFullScreenMethodID, m_applicationClass, "setFullScreen", "(Z)V"); + +#ifdef ANDROID_PLUGIN_OPENGL + FIND_AND_CHECK_CLASS("android/view/Surface"); +#if __ANDROID_API__ < 9 +# define ANDROID_VIEW_SURFACE_JNI_ID "mSurface" +#else +# define ANDROID_VIEW_SURFACE_JNI_ID "mNativeSurface" +#endif + GET_AND_CHECK_FIELD(m_surfaceFieldID, clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I"); +#endif + + jmethodID methodID; + GET_AND_CHECK_STATIC_METHOD(methodID, m_applicationClass, "activity", "()Landroid/app/Activity;"); + jobject activityObject = env->CallStaticObjectMethod(m_applicationClass, methodID); + m_activityObject = env->NewGlobalRef(activityObject); + 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;"); + + 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(activityObject, methodID)); + + GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;"); + m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(activityObject, 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; +} + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) +{ + typedef union { + JNIEnv *nativeEnvironment; + void *venv; + } UnionJNIEnvToVoid; + + __android_log_print(ANDROID_LOG_INFO, "Qt", "qt start"); + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + m_javaVM = 0; + + 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) + || !QtAndroidClipboard::registerNatives(env) + || !QtAndroidMenu::registerNatives(env)) { + __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); + return -1; + } + + m_javaVM = vm; + return JNI_VERSION_1_4; +} -- cgit v1.2.3