qtbase/src/plugins/platforms/android/androidjnimain.cpp
Frederik Gladhorn 77da617dc8 Merge remote-tracking branch 'origin/5.5' into dev
Conflicts:
	doc/global/qt-cpp-defines.qdocconf
	src/3rdparty/forkfd/forkfd.c
	src/corelib/codecs/qtextcodec.cpp
	src/corelib/kernel/qmetatype.cpp
	src/corelib/tools/qset.qdoc
	src/gui/accessible/qaccessible.cpp
	src/gui/image/qpixmapcache.cpp
	src/opengl/qgl.cpp
	src/tools/qdoc/generator.cpp
	src/widgets/kernel/qwidget.cpp
	tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp

Change-Id: I4fbe1fa756a54c6843aa75f4ef70a1069ba7b085
2015-08-06 10:54:01 +02:00

819 lines
30 KiB
C++

/****************************************************************************
**
** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL21$
** 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 http://www.qt.io/terms-conditions. For further
** information use the contact form at http://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 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <dlfcn.h>
#include <pthread.h>
#include <qplugin.h>
#include <qdebug.h>
#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 <android/bitmap.h>
#include <android/asset_manager_jni.h>
#include "qandroideventdispatcher.h"
#include <android/api-level.h>
#include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/private/qjni_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qwindowsysteminterface.h>
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 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<QByteArray> m_applicationParams;
struct SurfaceData
{
~SurfaceData() { delete surface; }
QJNIObjectPrivate *surface = nullptr;
AndroidSurfaceClient *client = nullptr;
};
QHash<int, AndroidSurfaceClient *> 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 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;
}
JavaVM *javaVM()
{
return m_javaVM;
}
AAssetManager *assetManager()
{
return m_assetManager;
}
jclass applicationClass()
{
return m_applicationClass;
}
jobject activity()
{
return m_activityObject;
}
void showStatusBar()
{
if (m_statusBarShowing)
return;
QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "setFullScreen", "(Z)V", false);
m_statusBarShowing = true;
}
void hideStatusBar()
{
if (!m_statusBarShowing)
return;
QJNIObjectPrivate::callStaticMethod<void>(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<uchar *>(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 + QStringLiteral(" ") + 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<void>(m_applicationClass,
"insertNativeView",
"(ILandroid/view/View;IIII)V",
surfaceId,
view,
x,
y,
qMax(w, 1),
qMax(h, 1));
return surfaceId;
}
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<void>(m_applicationClass,
"bringChildToFront",
"(I)V",
surfaceId);
}
void bringChildToBack(int surfaceId)
{
if (surfaceId == -1)
return;
QJNIObjectPrivate::callStaticMethod<void>(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<const char *> params(m_applicationParams.size());
for (int i = 0; i < m_applicationParams.size(); i++)
params[i] = static_cast<const char *>(m_applicationParams[i].constData());
int ret = m_main(m_applicationParams.length(), const_cast<char **>(params.data()));
Q_UNUSED(ret);
if (m_mainLibraryHnd) {
int res = dlclose(m_mainLibraryHnd);
if (res < 0)
qWarning() << "dlclose failed:" << dlerror();
}
if (m_applicationClass)
QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V");
// All attached threads should be detached before returning from this function.
JavaVM *vm = QtAndroidPrivate::javaVM();
if (vm != 0)
vm->DetachCurrentThread();
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<QByteArray> envVars = QByteArray(nativeString).split('\t');
env->ReleaseStringUTFChars(environmentString, nativeString);
foreach (const QByteArray &envVar, envVars) {
const QList<QByteArray> 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 (m_mainLibraryHnd == nullptr) {
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, 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*/)
{
env->DeleteGlobalRef(m_applicationClass);
env->DeleteGlobalRef(m_classLoaderObject);
if (m_resourcesObj)
env->DeleteGlobalRef(m_resourcesObj);
if (m_activityObject)
env->DeleteGlobalRef(m_activityObject);
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;
}
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)
{
// 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;
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<QAndroidPlatformScreen *>(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", "(IIIIDDD)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<jclass>(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, "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 (activityObject) {
m_activityObject = env->NewGlobalRef(activityObject);
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<jclass>(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<jclass>(env->NewGlobalRef(clazz));
GET_AND_CHECK_METHOD(m_bitmapDrawableConstructorMethodID,
m_bitmapDrawableClass,
"<init>",
"(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;
}