diff options
48 files changed, 4384 insertions, 411 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 66ea5eef79..31ba8272fc 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -979,6 +979,8 @@ qt_internal_extend_target(Core CONDITION ANDROID AND NOT ANDROID_EMBEDDED io/qstandardpaths_android.cpp io/qstorageinfo_unix.cpp kernel/qjni.cpp kernel/qjni_p.h + kernel/qjnienvironment.cpp kernel/qjnienvironment.h + kernel/qjniobject.cpp kernel/qjniobject.h kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h kernel/qjnionload.cpp ) diff --git a/src/corelib/doc/snippets/jni/src_qjniobject.cpp b/src/corelib/doc/snippets/jni/src_qjniobject.cpp new file mode 100644 index 0000000000..f3433134db --- /dev/null +++ b/src/corelib/doc/snippets/jni/src_qjniobject.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** + ** + ** Copyright (C) 2021 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the documentation of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:BSD$ + ** 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. + ** + ** BSD License Usage + ** Alternatively, you may use this file under the terms of the BSD license + ** as follows: + ** + ** "Redistribution and use in source and binary forms, with or without + ** modification, are permitted provided that the following conditions are + ** met: + ** * Redistributions of source code must retain the above copyright + ** notice, this list of conditions and the following disclaimer. + ** * Redistributions in binary form must reproduce the above copyright + ** notice, this list of conditions and the following disclaimer in + ** the documentation and/or other materials provided with the + ** distribution. + ** * Neither the name of The Qt Company Ltd nor the names of its + ** contributors may be used to endorse or promote products derived + ** from this software without specific prior written permission. + ** + ** + ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +//! [Working with lists] +QStringList getTrackTitles(const QJniObject &album) { + QStringList stringList; + QJniObject list = album.callObjectMethod("getTitles", + "()Ljava/util/List;"); + + if (list.isValid()) { + const int size = list.callMethod<jint>("size"); + for (int i = 0; i < size; ++i) { + QJniObject title = list.callObjectMethod("get", "(I)Ljava/lang/Object;", i); + stringList.append(title.toString()); + } + } + return stringList; +} +//! [Working with lists] + +//! [QJniObject scope] +void functionScope() +{ + QString helloString("Hello"); + jstring myJString = 0; + { + QJniObject string = QJniObject::fromString(helloString); + myJString = string.object<jstring>(); + } + + // Ops! myJString is no longer valid. +} +//! [QJniObject scope] + +//! [Registering native methods] +static void fromJavaOne(JNIEnv *env, jobject thiz, jint x) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + qDebug() << x << "< 100"; +} + +static void fromJavaTwo(JNIEnv *env, jobject thiz, jint x) +{ + Q_UNUSED(env); + Q_UNUSED(thiz); + qDebug() << x << ">= 100"; +} + +void registerNativeMethods() { + JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)}, + {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}}; + + QJniObject javaClass("my/java/project/FooJavaClass"); + QJniEnvironment env; + jclass objectClass = env->GetObjectClass(javaClass.object<jobject>()); + env->RegisterNatives(objectClass, + methods, + sizeof(methods) / sizeof(methods[0])); + env->DeleteLocalRef(objectClass); +} + +void foo() +{ + QJniObject::callStaticMethod<void>("my/java/project/FooJavaClass", "foo", "(I)V", 10); // Output: 10 < 100 + QJniObject::callStaticMethod<void>("my/java/project/FooJavaClass", "foo", "(I)V", 100); // Output: 100 >= 100 +} + +//! [Registering native methods] + +//! [Java native methods] +class FooJavaClass +{ + public static void foo(int x) + { + if (x < 100) + callNativeOne(x); + else + callNativeTwo(x); + } + +private static native void callNativeOne(int x); +private static native void callNativeTwo(int x); + +} +//! [Java native methods] diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp index 139c8f3a30..c046b8078d 100644 --- a/src/corelib/global/qglobal.cpp +++ b/src/corelib/global/qglobal.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2017 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -78,7 +78,7 @@ #endif #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) -#include <private/qjni_p.h> +#include <qjniobject.h> #endif #if defined(Q_OS_SOLARIS) @@ -2296,7 +2296,7 @@ Oreo // https://source.android.com/source/build-numbers.html // https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels - const int sdk_int = QJNIObjectPrivate::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT"); + const int sdk_int = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT"); return &versions_string[versions_indices[qBound(0, sdk_int, versions_count - 1)]]; } #endif diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp index a9388a9795..b6ab4435a7 100644 --- a/src/corelib/global/qoperatingsystemversion.cpp +++ b/src/corelib/global/qoperatingsystemversion.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -50,7 +50,7 @@ #include <qdebug.h> #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) -#include <private/qjni_p.h> +#include <QJniObject> #endif QT_BEGIN_NAMESPACE @@ -157,7 +157,7 @@ QOperatingSystemVersion QOperatingSystemVersion::current() version.m_os = currentType(); #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) #ifndef QT_BOOTSTRAPPED - const QVersionNumber v = QVersionNumber::fromString(QJNIObjectPrivate::getStaticObjectField( + const QVersionNumber v = QVersionNumber::fromString(QJniObject::getStaticObjectField( "android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").toString()); if (!v.isNull()) { version.m_major = v.majorVersion(); @@ -207,7 +207,7 @@ QOperatingSystemVersion QOperatingSystemVersion::current() }; // This will give us at least the first 2 version components - const size_t versionIdx = size_t(QJNIObjectPrivate::getStaticField<jint>( + const size_t versionIdx = size_t(QJniObject::getStaticField<jint>( "android/os/Build$VERSION", "SDK_INT")) - 1; if (versionIdx < sizeof(versions) / sizeof(versions[0])) { version.m_major = versions[versionIdx].major; diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp index ca16566c1c..eb7acc5edf 100644 --- a/src/corelib/global/qrandom.cpp +++ b/src/corelib/global/qrandom.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2020 Intel Corporation. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -72,10 +73,6 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando } #endif -#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) -# include <private/qjni_p.h> -#endif - // This file is too low-level for regular Q_ASSERT (the logging framework may // recurse back), so use regular assert() #undef NDEBUG diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp index 3aa310e168..46f96e4037 100644 --- a/src/corelib/io/qstandardpaths_android.cpp +++ b/src/corelib/io/qstandardpaths_android.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -41,7 +41,7 @@ #ifndef QT_NO_STANDARDPATHS -#include <QtCore/private/qjni_p.h> +#include <QJniObject> #include <QtCore/private/qjnihelpers_p.h> #include <QtCore/qmap.h> #include <QDir> @@ -57,13 +57,13 @@ static QString testDir() : QLatin1String(""); } -static QJNIObjectPrivate applicationContext() +static QJniObject applicationContext() { - static QJNIObjectPrivate appCtx; + static QJniObject appCtx; if (appCtx.isValid()) return appCtx; - QJNIObjectPrivate context(QtAndroidPrivate::activity()); + QJniObject context(QtAndroidPrivate::activity()); if (!context.isValid()) { context = QtAndroidPrivate::service(); if (!context.isValid()) @@ -75,10 +75,10 @@ static QJNIObjectPrivate applicationContext() return appCtx; } -static inline QString getAbsolutePath(const QJNIObjectPrivate &file) +static inline QString getAbsolutePath(const QJniObject &file) { - QJNIObjectPrivate path = file.callObjectMethod("getAbsolutePath", - "()Ljava/lang/String;"); + QJniObject path = file.callObjectMethod("getAbsolutePath", + "()Ljava/lang/String;"); if (!path.isValid()) return QString(); @@ -95,22 +95,22 @@ static QString getExternalFilesDir(const char *directoryField = nullptr) if (!path.isEmpty()) return path; - QJNIObjectPrivate appCtx = applicationContext(); + QJniObject appCtx = applicationContext(); if (!appCtx.isValid()) return QString(); - QJNIObjectPrivate dirField = QJNIObjectPrivate::fromString(QLatin1String("")); + QJniObject dirField = QJniObject::fromString(QLatin1String("")); if (directoryField && strlen(directoryField) > 0) { - dirField = QJNIObjectPrivate::getStaticObjectField("android/os/Environment", - directoryField, - "Ljava/lang/String;"); + dirField = QJniObject::getStaticObjectField("android/os/Environment", + directoryField, + "Ljava/lang/String;"); if (!dirField.isValid()) return QString(); } - QJNIObjectPrivate file = appCtx.callObjectMethod("getExternalFilesDir", - "(Ljava/lang/String;)Ljava/io/File;", - dirField.object()); + QJniObject file = appCtx.callObjectMethod("getExternalFilesDir", + "(Ljava/lang/String;)Ljava/io/File;", + dirField.object()); if (!file.isValid()) return QString(); @@ -128,12 +128,12 @@ static QString getExternalCacheDir() if (!path.isEmpty()) return path; - QJNIObjectPrivate appCtx = applicationContext(); + QJniObject appCtx = applicationContext(); if (!appCtx.isValid()) return QString(); - QJNIObjectPrivate file = appCtx.callObjectMethod("getExternalCacheDir", - "()Ljava/io/File;"); + QJniObject file = appCtx.callObjectMethod("getExternalCacheDir", + "()Ljava/io/File;"); if (!file.isValid()) return QString(); @@ -150,12 +150,12 @@ static QString getCacheDir() if (!path.isEmpty()) return path; - QJNIObjectPrivate appCtx = applicationContext(); + QJniObject appCtx = applicationContext(); if (!appCtx.isValid()) return QString(); - QJNIObjectPrivate file = appCtx.callObjectMethod("getCacheDir", - "()Ljava/io/File;"); + QJniObject file = appCtx.callObjectMethod("getCacheDir", + "()Ljava/io/File;"); if (!file.isValid()) return QString(); @@ -172,12 +172,12 @@ static QString getFilesDir() if (!path.isEmpty()) return path; - QJNIObjectPrivate appCtx = applicationContext(); + QJniObject appCtx = applicationContext(); if (!appCtx.isValid()) return QString(); - QJNIObjectPrivate file = appCtx.callObjectMethod("getFilesDir", - "()Ljava/io/File;"); + QJniObject file = appCtx.callObjectMethod("getFilesDir", + "()Ljava/io/File;"); if (!file.isValid()) return QString(); diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index d9a74c2828..781791c971 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -91,8 +91,8 @@ #endif // QT_NO_QOBJECT #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) -# include <private/qjni_p.h> -# include <private/qjnihelpers_p.h> +#include <QJniObject> +#include <private/qjnihelpers_p.h> #endif #ifdef Q_OS_MAC @@ -170,17 +170,17 @@ QString QCoreApplicationPrivate::appVersion() const # ifdef Q_OS_DARWIN applicationVersion = infoDictionaryStringProperty(QStringLiteral("CFBundleVersion")); # elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) - QJNIObjectPrivate context(QtAndroidPrivate::context()); + QJniObject context(QtAndroidPrivate::context()); if (context.isValid()) { - QJNIObjectPrivate pm = context.callObjectMethod( + QJniObject pm = context.callObjectMethod( "getPackageManager", "()Landroid/content/pm/PackageManager;"); - QJNIObjectPrivate pn = context.callObjectMethod<jstring>("getPackageName"); + QJniObject pn = context.callObjectMethod<jstring>("getPackageName"); if (pm.isValid() && pn.isValid()) { - QJNIObjectPrivate packageInfo = pm.callObjectMethod( + QJniObject packageInfo = pm.callObjectMethod( "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;", pn.object(), 0); if (packageInfo.isValid()) { - QJNIObjectPrivate versionName = packageInfo.getObjectField( + QJniObject versionName = packageInfo.getObjectField( "versionName", "Ljava/lang/String;"); if (versionName.isValid()) return versionName.toString(); diff --git a/src/corelib/kernel/qjni_p.h b/src/corelib/kernel/qjni_p.h index edca36e2bd..57ec8a39b5 100644 --- a/src/corelib/kernel/qjni_p.h +++ b/src/corelib/kernel/qjni_p.h @@ -47,6 +47,7 @@ // // We mean it. // +// FIXME: Remove this once the JNI API is used by other modules. #ifndef QJNI_P_H #define QJNI_P_H diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp new file mode 100644 index 0000000000..04676cc29c --- /dev/null +++ b/src/corelib/kernel/qjnienvironment.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 "qjnienvironment.h" +#include "qjniobject.h" +#include "qjnihelpers_p.h" + +#include <QtCore/QThread> +#include <QtCore/QThreadStorage> + +QT_BEGIN_NAMESPACE + +/*! + \class QJniEnvironment + \inmodule QtCore + \brief The QJniEnvironment provides access to the JNI Environment. + \since 6.1 + + \note This API has been tested and meant to be mainly used for Android and it hasn't been tested + for other platforms. +*/ + +static const char qJniThreadName[] = "QtThread"; + +class QJniEnvironmentPrivate +{ +public: + JNIEnv *jniEnv = nullptr; +}; + +class QJniEnvironmentPrivateTLS +{ +public: + inline ~QJniEnvironmentPrivateTLS() + { + QtAndroidPrivate::javaVM()->DetachCurrentThread(); + } +}; + +struct QJniLocalRefDeleterPrivate +{ + static void cleanup(jobject obj) + { + if (!obj) + return; + + QJniEnvironment env; + env->DeleteLocalRef(obj); + } +}; + +// To simplify this we only define it for jobjects. +typedef QScopedPointer<_jobject, QJniLocalRefDeleterPrivate> QJniScopedLocalRefPrivate; + + +Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS) + + + +/*! + \fn QJniEnvironment::QJniEnvironment() + + Constructs a new QJniEnvironment object and attaches the current thread to the Java VM. +*/ +QJniEnvironment::QJniEnvironment() + : d(new QJniEnvironmentPrivate{}) +{ + JavaVM *vm = QtAndroidPrivate::javaVM(); + const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6); + if (ret == JNI_OK) // Already attached + return; + + if (ret == JNI_EDETACHED) { // We need to (re-)attach + JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, nullptr }; + if (vm->AttachCurrentThread(&d->jniEnv, &args) != JNI_OK) + return; + + if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it. + jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS); + } +} + +/*! + \fn QJniEnvironment::~QJniEnvironment() + + Detaches the current thread from the Java VM and destroys the QJniEnvironment object. +*/ +QJniEnvironment::~QJniEnvironment() +{ + exceptionCheckAndClear(); +} + +/*! + \fn JNIEnv *QJniEnvironment::operator->() + + Provides access to the QJniEnvironment's JNIEnv pointer. +*/ +JNIEnv *QJniEnvironment::operator->() +{ + return d->jniEnv; +} + +/*! + \fn QJniEnvironment::operator JNIEnv *() const + + Returns the JNI Environment pointer. +*/ +QJniEnvironment::operator JNIEnv* () const +{ + return d->jniEnv; +} + +/*! + \fn jclass QJniEnvironment::findClass(const char *className) + + Searches for \a className using all available class loaders. Qt on Android + uses a custom class loader to load all the .jar files and it must be used + to find any classes that are created by that class loader because these + classes are not visible in the default class loader. + + Returns the class pointer or null if is not found. + + A use case for this function is searching for a custom class then calling + its memeber method. The following code snippet create an instance of the + class \c CustomClass and then calls \c printFromJava() method: + + \code + QJniEnvironment env; + jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass"); + QJniObject classObject(javaClass); + + QJniObject javaMessage = QJniObject::fromString("findClass example"); + classObject.callMethod<void>("printFromJava", + "(Ljava/lang/String;)V", + javaMessage.object<jstring>()); + \endcode + + \since Qt 6.1 +*/ +jclass QJniEnvironment::findClass(const char *className) +{ + return QtAndroidPrivate::findClass(className, d->jniEnv); +} + +/*! + \fn JavaVM *QJniEnvironment::javaVM() + + Returns the Java VM interface. + + \since Qt 6.1 +*/ +JavaVM *QJniEnvironment::javaVM() +{ + return QtAndroidPrivate::javaVM(); +} + +/*! + \fn bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[]) + + Registers the Java methods \a methods that can call native C++ functions from class \a + className. These methods must be registered before any attempt to call them. + + Returns True if the registration is successful, otherwise False. + + Each element in the methods array consists of: + \list + \li The Java method name + \li Method signature + \li The C++ functions that will be executed + \endlist + + \code + JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)}, + {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}}; + QJniEnvironment env; + env.registerNativeMethods("org/qtproject/android/TestJavaClass", methods); + \endcode + + \since Qt 6.1 +*/ +bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[], int size) +{ + jclass clazz = findClass(className); + + if (!clazz) + return false; + + jclass gClazz = static_cast<jclass>(d->jniEnv->NewGlobalRef(clazz)); + + if (d->jniEnv->RegisterNatives(gClazz, methods, size / sizeof(methods[0])) < 0) { + exceptionCheckAndClear(); + return false; + } + + d->jniEnv->DeleteLocalRef(gClazz); + + return true; +} + +/*! + \enum QJniExceptionCleaner::OutputMode + + \value Silent the exceptions are cleaned silently + \value Verbose describes the exceptions before cleaning them +*/ + +/*! + \fn QJniEnvironment::exceptionCheckAndClear(OutputMode outputMode = OutputMode::Silent) + + Cleans any pending exceptions either silently or with descriptions, depending on the \a outputMode. + + \since 6.1 +*/ +bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputMode) +{ + if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) { + if (outputMode != OutputMode::Silent) + d->jniEnv->ExceptionDescribe(); + d->jniEnv->ExceptionClear(); + + return true; + } + + return false; +} + +/*! + \fn QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Silent) + + Cleans any pending exceptions for \a env, either silently or with descriptions, depending on the \a outputMode. + + \since 6.1 +*/ +bool QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, QJniEnvironment::OutputMode outputMode) +{ + if (Q_UNLIKELY(env->ExceptionCheck())) { + if (outputMode != OutputMode::Silent) + env->ExceptionDescribe(); + env->ExceptionClear(); + + return true; + } + + return false; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h new file mode 100644 index 0000000000..e19a3935da --- /dev/null +++ b/src/corelib/kernel/qjnienvironment.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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$ +** +****************************************************************************/ + +#ifndef QJNI_ENVIRONMENT_H +#define QJNI_ENVIRONMENT_H + +#include <QScopedPointer> + +#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#include <jni.h> +#else +class JNIEnv; +class JNINativeMethod; +class JavaVM; +class jclass; +#endif + +QT_BEGIN_NAMESPACE + +class QJniEnvironmentPrivate; + +class Q_CORE_EXPORT QJniEnvironment +{ +public: + QJniEnvironment(); + ~QJniEnvironment(); + JNIEnv *operator->(); + operator JNIEnv *() const; + jclass findClass(const char *className); + static JavaVM *javaVM(); + bool registerNativeMethods(const char *className, JNINativeMethod methods[], int size); + + enum class OutputMode { + Silent, + Verbose + }; + + bool exceptionCheckAndClear(OutputMode outputMode = OutputMode::Verbose); + static bool exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose); + + +private: + Q_DISABLE_COPY_MOVE(QJniEnvironment) + QScopedPointer<QJniEnvironmentPrivate> d; +}; + +QT_END_NAMESPACE + +#endif // QJNI_ENVIRONMENT_H diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp index 14f9f389e8..45f4a4d895 100644 --- a/src/corelib/kernel/qjnihelpers.cpp +++ b/src/corelib/kernel/qjnihelpers.cpp @@ -37,14 +37,16 @@ ** ****************************************************************************/ +#include "qcoreapplication.h" +#include "qjnienvironment.h" #include "qjnihelpers_p.h" -#include "qjni_p.h" -#include "qmutex.h" +#include "qjniobject.h" #include "qlist.h" +#include "qmutex.h" #include "qsemaphore.h" #include "qsharedpointer.h" #include "qthread.h" -#include "qcoreapplication.h" + #include <QtCore/qrunnable.h> #include <deque> @@ -146,7 +148,7 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requ std::unique_ptr<jint[]> results(new jint[size]); env->GetIntArrayRegion(grantResults, 0, size, results.get()); for (int i = 0 ; i < size; ++i) { - const auto &permission = QJNIObjectPrivate(env->GetObjectArrayElement(permissions, i)).toString(); + const auto &permission = QJniObject(env->GetObjectArrayElement(permissions, i)).toString(); auto value = results[i] == PERMISSION_GRANTED ? QtAndroidPrivate::PermissionsResult::Granted : QtAndroidPrivate::PermissionsResult::Denied; @@ -286,27 +288,14 @@ void QtAndroidPrivate::handleResume() listeners.at(i)->handleResume(); } -static inline bool exceptionCheck(JNIEnv *env) -{ - if (env->ExceptionCheck()) { -#ifdef QT_DEBUG - env->ExceptionDescribe(); -#endif // QT_DEBUG - env->ExceptionClear(); - return true; - } - - return false; -} - static void setAndroidSdkVersion(JNIEnv *env) { jclass androidVersionClass = env->FindClass("android/os/Build$VERSION"); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return; jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I"); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return; g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID); @@ -342,42 +331,42 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) { jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative"); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; jmethodID activityMethodID = env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;"); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative, "service", "()Landroid/app/Service;"); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative, "classLoader", "()Ljava/lang/ClassLoader;"); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID); - if (exceptionCheck(env)) + if (QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; setAndroidSdkVersion(env); @@ -405,7 +394,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK); - if (!regOk && exceptionCheck(env)) + if (!regOk && QJniEnvironment::exceptionCheckAndClear(env)) return JNI_ERR; g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative, @@ -495,7 +484,10 @@ void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable & waitForSemaphore(timeoutMs, sem); } -void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permissions, const QtAndroidPrivate::PermissionsResultFunc &callbackFunc, bool directCall) +void QtAndroidPrivate::requestPermissions(JNIEnv *env, + const QStringList &permissions, + const QtAndroidPrivate::PermissionsResultFunc &callbackFunc, + bool directCall) { if (androidSdkVersion() < 23 || !activity()) { QHash<QString, QtAndroidPrivate::PermissionsResult> res; @@ -517,12 +509,17 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permis (*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc); } - QJNIEnvironmentPrivate env; - auto array = env->NewObjectArray(permissions.size(), env->FindClass("java/lang/String"), nullptr); + QJniEnvironment env; + jclass clazz = env->FindClass("java/lang/String"); + + if (env.exceptionCheckAndClear()) + return; + + auto array = env->NewObjectArray(permissions.size(), clazz, nullptr); int index = 0; for (const auto &perm : permissions) - env->SetObjectArrayElement(array, index++, QJNIObjectPrivate::fromString(perm).object()); - QJNIObjectPrivate(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode); + env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object()); + QJniObject(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode); env->DeleteLocalRef(array); }, env); } @@ -543,10 +540,10 @@ QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEn QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission) { - const auto res = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt/android/QtNative", - "checkSelfPermission", - "(Ljava/lang/String;)I", - QJNIObjectPrivate::fromString(permission).object()); + const auto res = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative", + "checkSelfPermission", + "(Ljava/lang/String;)I", + QJniObject::fromString(permission).object()); return res == PERMISSION_GRANTED ? PermissionsResult::Granted : PermissionsResult::Denied; } @@ -555,8 +552,9 @@ bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permi if (androidSdkVersion() < 23 || !activity()) return false; - return QJNIObjectPrivate(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale", "(Ljava/lang/String;)Z", - QJNIObjectPrivate::fromString(permission).object()); + return QJniObject(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale", + "(Ljava/lang/String;)Z", + QJniObject::fromString(permission).object()); } void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener) diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h index c849013ba2..a0d9c219d7 100644 --- a/src/corelib/kernel/qjnihelpers_p.h +++ b/src/corelib/kernel/qjnihelpers_p.h @@ -119,6 +119,7 @@ namespace QtAndroidPrivate Q_CORE_EXPORT jobject context(); Q_CORE_EXPORT JavaVM *javaVM(); Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env); + Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env); jobject classLoader(); Q_CORE_EXPORT jint androidSdkVersion(); Q_CORE_EXPORT void runOnAndroidThread(const Runnable &runnable, JNIEnv *env); diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp new file mode 100644 index 0000000000..ba7d0af778 --- /dev/null +++ b/src/corelib/kernel/qjniobject.cpp @@ -0,0 +1,1872 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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 "qjnienvironment.h" +#include "qjnihelpers_p.h" +#include "qjniobject.h" + +#include <QtCore/QReadWriteLock> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +/*! + \class QJniObject + \inmodule QtCore + \brief Provides a convenient set of APIs to call Java code from C++ using the Java Native Interface (JNI). + \since 6.1 + + \sa QJniEnvironment + + \section1 General Notes + + \list + \li Class names needs to contain the fully-qualified class name, for example: \b"java/lang/String". + \li Method signatures are written as \b"(Arguments)ReturnType" + \li All object types are returned as a QJniObject. + \endlist + + \note This API has been tested and meant to be mainly used for Android and it hasn't been tested + for other platforms. + + \section1 Method Signatures + + For functions that take no arguments, QJniObject provides convenience functions that will use + the correct signature based on the provided template type. For example: + + \code + jint x = QJniObject::callMethod<jint>("getSize"); + QJniObject::callMethod<void>("touch"); + \endcode + + In other cases you will need to supply the signature yourself, and it is important that the + signature matches the function you want to call. The signature structure is \b \(A\)R, where \b A + is the type of the argument\(s\) and \b R is the return type. Array types in the signature must + have the \b\[ suffix and the fully-qualified type names must have the \b L prefix and \b ; suffix. + + The example below demonstrates how to call two different static functions. + \code + // Java class + package org.qtproject.qt; + class TestClass + { + static String fromNumber(int x) { ... } + static String[] stringArray(String s1, String s2) { ... } + } + \endcode + + The signature for the first function is \b"\(I\)Ljava/lang/String;" + + \code + // C++ code + QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass", + "fromNumber" + "(I)Ljava/lang/String;", + 10); + \endcode + + and the signature for the second function is \b"\(Ljava/lang/String;Ljava/lang/String;\)\[Ljava/lang/String;" + + \code + // C++ code + QJniObject string1 = QJniObject::fromString("String1"); + QJniObject string2 = QJniObject::fromString("String2"); + QJniObject stringArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass", + "stringArray" + "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;" + string1.object<jstring>(), + string2.object<jstring>()); + \endcode + + + \section1 Handling Java Exception + + When calling Java functions that might throw an exception, it is important that you check, handle + and clear out the exception before continuing. + + \note It is unsafe to make a JNI call when there are exceptions pending. For more information, + see QJniEnvironment::exceptionCheckAndClear(). + + \section1 Java Native Methods + + Java native methods makes it possible to call native code from Java, this is done by creating a + function declaration in Java and prefixing it with the \b native keyword. + Before a native function can be called from Java, you need to map the Java native function to a + native function in your code. Mapping functions can be done by calling the RegisterNatives() function + through the \l{QJniEnvironment}{JNI environment pointer}. + + The example below demonstrates how this could be done. + + Java implementation: + \snippet jni/src_qjniobject.cpp Java native methods + + C++ Implementation: + \snippet jni/src_qjniobject.cpp Registering native methods + + \section1 The Lifetime of a Java Object + + Most \l{Object types}{objects} received from Java will be local references and will only stay valid + in the scope you received them. After that, the object becomes eligible for garbage collection. If you + want to keep a Java object alive you need to either create a new global reference to the object and + release it when you are done, or construct a new QJniObject and let it manage the lifetime of the Java object. + \sa object() + + \note The QJniObject only manages its own references, if you construct a QJniObject from a + global or local reference that reference will not be released by the QJniObject. + + \section1 JNI Types + + \section2 Object Types + \table + \header + \li Type + \li Signature + \row + \li jobject + \li Ljava/lang/Object; + \row + \li jclass + \li Ljava/lang/Class; + \row + \li jstring + \li Ljava/lang/String; + \row + \li jthrowable + \li Ljava/lang/Throwable; + \row + \li jobjectArray + \li [Ljava/lang/Object; + \row + \li jarray + \li [\e<type> + \row + \li jbooleanArray + \li [Z + \row + \li jbyteArray + \li [B + \row + \li jcharArray + \li [C + \row + \li jshortArray + \li [S + \row + \li jintArray + \li [I + \row + \li jlongArray + \li [J + \row + \li jfloatArray + \li [F + \row + \li jdoubleArray + \li [D + \endtable + + \section2 Primitive Types + \table + \header + \li Type + \li Signature + \row + \li jboolean + \li Z + \row + \li jbyte + \li B + \row + \li jchar + \li C + \row + \li jshort + \li S + \row + \li jint + \li I + \row + \li jlong + \li J + \row + \li jfloat + \li F + \row + \li jdouble + \li D + \endtable + + \section2 Other + \table + \header + \li Type + \li Signature + \row + \li void + \li V + \row + \li \e{Custom type} + \li L\e<fully-qualified-name>; + \endtable + + For more information about JNI see: \l http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html +*/ + +/*! + \fn bool operator==(const QJniObject &o1, const QJniObject &o2) + + \relates QJniObject + + Returns true if both objects, \a o1 and \a o2, are referencing the same Java object, or if both + are NULL. In any other cases false will be returned. +*/ + +/*! + \fn bool operator!=(const QJniObject &o1, const QJniObject &o2) + \relates QJniObject + + Returns true if \a o1 holds a reference to a different object than \a o2. +*/ + +static inline QLatin1String keyBase() +{ + return QLatin1String("%1%2:%3"); +} + +static QString qt_convertJString(jstring string) +{ + QJniEnvironment env; + int strLength = env->GetStringLength(string); + QString res(strLength, Qt::Uninitialized); + env->GetStringRegion(string, 0, strLength, reinterpret_cast<jchar *>(res.data())); + return res; +} + +typedef QHash<QString, jclass> JClassHash; +Q_GLOBAL_STATIC(JClassHash, cachedClasses) +Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock) + +static QByteArray toBinaryEncClassName(const QByteArray &className) +{ + return QByteArray(className).replace('/', '.'); +} + +static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nullptr) +{ + QReadLocker locker(cachedClassesLock); + const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(QString::fromLatin1(classBinEnc)); + const bool found = (it != cachedClasses->constEnd()); + + if (isCached) + *isCached = found; + + return found ? it.value() : 0; +} + +inline static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false) +{ + const QByteArray &binEncClassName = binEncoded ? className : toBinaryEncClassName(className); + + bool isCached = false; + jclass clazz = getCachedClass(binEncClassName, &isCached); + if (clazz || isCached) + return clazz; + + QJniObject classLoader(QtAndroidPrivate::classLoader()); + if (!classLoader.isValid()) + return nullptr; + + QWriteLocker locker(cachedClassesLock); + // did we lose the race? + const QLatin1String key(binEncClassName); + const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key); + if (it != cachedClasses->constEnd()) + return it.value(); + + QJniObject stringName = QJniObject::fromString(key); + QJniObject classObject = classLoader.callObjectMethod("loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;", + stringName.object()); + + if (!QJniEnvironment::exceptionCheckAndClear(env) && classObject.isValid()) + clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object())); + + cachedClasses->insert(key, clazz); + return clazz; +} + +typedef QHash<QString, jmethodID> JMethodIDHash; +Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID) +Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock) + +static inline jmethodID getMethodID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig, + bool isStatic = false) +{ + jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, sig) + : env->GetMethodID(clazz, name, sig); + + if (QJniEnvironment::exceptionCheckAndClear(env)) + return nullptr; + + return id; +} + +static jmethodID getCachedMethodID(JNIEnv *env, + jclass clazz, + const QByteArray &className, + const char *name, + const char *sig, + bool isStatic = false) +{ + if (className.isEmpty()) + return getMethodID(env, clazz, name, sig, isStatic); + + const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig)); + QHash<QString, jmethodID>::const_iterator it; + + { + QReadLocker locker(cachedMethodIDLock); + it = cachedMethodID->constFind(key); + if (it != cachedMethodID->constEnd()) + return it.value(); + } + + { + QWriteLocker locker(cachedMethodIDLock); + it = cachedMethodID->constFind(key); + if (it != cachedMethodID->constEnd()) + return it.value(); + + jmethodID id = getMethodID(env, clazz, name, sig, isStatic); + + cachedMethodID->insert(key, id); + return id; + } +} + +typedef QHash<QString, jfieldID> JFieldIDHash; +Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID) +Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock) + +static inline jfieldID getFieldID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig, + bool isStatic = false) +{ + jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, sig) + : env->GetFieldID(clazz, name, sig); + + if (QJniEnvironment::exceptionCheckAndClear(env)) + return nullptr; + + return id; +} + +static jfieldID getCachedFieldID(JNIEnv *env, + jclass clazz, + const QByteArray &className, + const char *name, + const char *sig, + bool isStatic = false) +{ + if (className.isNull()) + return getFieldID(env, clazz, name, sig, isStatic); + + const QString key = keyBase().arg(QLatin1String(className), QLatin1String(name), QLatin1String(sig)); + QHash<QString, jfieldID>::const_iterator it; + + { + QReadLocker locker(cachedFieldIDLock); + it = cachedFieldID->constFind(key); + if (it != cachedFieldID->constEnd()) + return it.value(); + } + + { + QWriteLocker locker(cachedFieldIDLock); + it = cachedFieldID->constFind(key); + if (it != cachedFieldID->constEnd()) + return it.value(); + + jfieldID id = getFieldID(env, clazz, name, sig, isStatic); + + cachedFieldID->insert(key, id); + return id; + } +} + +jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env) +{ + const QByteArray &classDotEnc = toBinaryEncClassName(className); + bool isCached = false; + jclass clazz = getCachedClass(classDotEnc, &isCached); + + const bool found = clazz || (!clazz && isCached); + + if (found) + return clazz; + + const QLatin1String key(classDotEnc); + if (env) { // We got an env. pointer (We expect this to be the right env. and call FindClass()) + QWriteLocker locker(cachedClassesLock); + const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key); + // Did we lose the race? + if (it != cachedClasses->constEnd()) + return it.value(); + + jclass fclazz = env->FindClass(className); + if (!QJniEnvironment::exceptionCheckAndClear(env)) { + clazz = static_cast<jclass>(env->NewGlobalRef(fclazz)); + env->DeleteLocalRef(fclazz); + } + + if (clazz) + cachedClasses->insert(key, clazz); + } + + if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader... + clazz = loadClass(classDotEnc, QJniEnvironment(), true); + + return clazz; +} + +class QJniObjectPrivate +{ +public: + QJniObjectPrivate() = default; + ~QJniObjectPrivate() { + QJniEnvironment env; + if (m_jobject) + env->DeleteGlobalRef(m_jobject); + if (m_jclass && m_own_jclass) + env->DeleteGlobalRef(m_jclass); + } + + jobject m_jobject = nullptr; + jclass m_jclass = nullptr; + bool m_own_jclass = true; + QByteArray m_className; +}; + +/*! + \fn QJniObject::QJniObject() + + Constructs an invalid QJniObject. + + \sa isValid() +*/ +QJniObject::QJniObject() + : d(new QJniObjectPrivate()) +{ +} + +/*! + \fn QJniObject::QJniObject(const char *className) + + Constructs a new QJniObject by calling the default constructor of \a className. + + \code + QJniObject myJavaString("java/lang/String"); + \endcode +*/ +QJniObject::QJniObject(const char *className) + : d(new QJniObjectPrivate()) +{ + QJniEnvironment env; + d->m_className = toBinaryEncClassName(className); + d->m_jclass = loadClass(d->m_className, env, true); + d->m_own_jclass = false; + if (d->m_jclass) { + // get default constructor + jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", "()V"); + if (constructorId) { + jobject obj = env->NewObject(d->m_jclass, constructorId); + if (obj) { + d->m_jobject = env->NewGlobalRef(obj); + env->DeleteLocalRef(obj); + } + } + } +} + +/*! + \fn QJniObject::QJniObject(const char *className, const char *signature, ...) + + Constructs a new QJniObject by calling the constructor of \a className with \a signature + and arguments. + + \code + QJniEnvironment env; + char* str = "Hello"; + jstring myJStringArg = env->NewStringUTF(str); + QJniObject myNewJavaString("java/lang/String", "(Ljava/lang/String;)V", myJStringArg); + \endcode +*/ +QJniObject::QJniObject(const char *className, const char *sig, ...) + : d(new QJniObjectPrivate()) +{ + QJniEnvironment env; + d->m_className = toBinaryEncClassName(className); + d->m_jclass = loadClass(d->m_className, env, true); + d->m_own_jclass = false; + if (d->m_jclass) { + jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", sig); + if (constructorId) { + va_list args; + va_start(args, sig); + jobject obj = env->NewObjectV(d->m_jclass, constructorId, args); + va_end(args); + if (obj) { + d->m_jobject = env->NewGlobalRef(obj); + env->DeleteLocalRef(obj); + } + } + } +} + +QJniObject::QJniObject(const char *className, const char *sig, const QVaListPrivate &args) + : d(new QJniObjectPrivate()) +{ + QJniEnvironment env; + d->m_className = toBinaryEncClassName(className); + d->m_jclass = loadClass(d->m_className, env, true); + d->m_own_jclass = false; + if (d->m_jclass) { + jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", sig); + if (constructorId) { + jobject obj = env->NewObjectV(d->m_jclass, constructorId, args); + if (obj) { + d->m_jobject = env->NewGlobalRef(obj); + env->DeleteLocalRef(obj); + } + } + } +} + +/*! + \fn QJniObject::QJniObject(jclass clazz, const char *signature, ...) + + Constructs a new QJniObject from \a clazz by calling the constructor with \a signature + and arguments. + + \code + QJniEnvironment env; + jclass myClazz = env.findClass("org/qtproject/qt/TestClass"); + QJniObject(myClazz, "(I)V", 3); + \endcode +*/ +QJniObject::QJniObject(jclass clazz, const char *sig, ...) + : d(new QJniObjectPrivate()) +{ + QJniEnvironment env; + if (clazz) { + d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz)); + if (d->m_jclass) { + jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", sig); + if (constructorId) { + va_list args; + va_start(args, sig); + jobject obj = env->NewObjectV(d->m_jclass, constructorId, args); + va_end(args); + if (obj) { + d->m_jobject = env->NewGlobalRef(obj); + env->DeleteLocalRef(obj); + } + } + } + } +} + +/*! + \fn QJniObject::QJniObject(jclass clazz) + + Constructs a new QJniObject by calling the default constructor of \a clazz. + + Note: The QJniObject will create a new reference to the class \a clazz + and releases it again when it is destroyed. References to the class created + outside the QJniObject need to be managed by the caller. +*/ + +QJniObject::QJniObject(jclass clazz) + : d(new QJniObjectPrivate()) +{ + QJniEnvironment env; + d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz)); + if (d->m_jclass) { + // get default constructor + jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", "()V"); + if (constructorId) { + jobject obj = env->NewObject(d->m_jclass, constructorId); + if (obj) { + d->m_jobject = env->NewGlobalRef(obj); + env->DeleteLocalRef(obj); + } + } + } +} + +QJniObject::QJniObject(jclass clazz, const char *sig, const QVaListPrivate &args) + : d(new QJniObjectPrivate()) +{ + QJniEnvironment env; + if (clazz) { + d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz)); + if (d->m_jclass) { + jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", sig); + if (constructorId) { + jobject obj = env->NewObjectV(d->m_jclass, constructorId, args); + if (obj) { + d->m_jobject = env->NewGlobalRef(obj); + env->DeleteLocalRef(obj); + } + } + } + } +} + +/*! + \fn QJniObject::QJniObject(jobject object) + + Constructs a new QJniObject around the Java object \a object. + + \note The QJniObject will hold a reference to the Java object \a object + and release it when destroyed. Any references to the Java object \a object + outside QJniObject needs to be managed by the caller. In most cases you + should never call this function with a local reference unless you intend + to manage the local reference yourself. See QJniObject::fromLocalRef() + for converting a local reference to a QJniObject. + + \sa fromLocalRef() +*/ +QJniObject::QJniObject(jobject obj) + : d(new QJniObjectPrivate()) +{ + if (!obj) + return; + + QJniEnvironment env; + d->m_jobject = env->NewGlobalRef(obj); + jclass cls = env->GetObjectClass(obj); + d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls)); + env->DeleteLocalRef(cls); +} + +/*! + \fn QJniObject::~QJniObject() + + Destroys the QJniObject and releases any references held by the QJniObject. +*/ +QJniObject::~QJniObject() +{} + +/*! + \fn template <typename T> T QJniObject::object() const + + Returns the object held by the QJniObject as type T. + + \code + QJniObject string = QJniObject::fromString("Hello, JNI"); + jstring jstring = string.object<jstring>(); + \endcode + + \note The returned object is still owned by the QJniObject. If you want to keep the object valid + you should create a new QJniObject or make a new global reference to the object and + free it yourself. + + \snippet jni/src_qjniobject.cpp QJniObject scope + + \code + jobject object = jniObject.object(); + \endcode +*/ +Q_CORE_EXPORT jobject QJniObject::object() const +{ + return javaObject(); +} + +QJniObject QJniObject::callObjectMethodV(const char *methodName, + const char *sig, + va_list args) const +{ + QJniEnvironment env; + jobject res = nullptr; + jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig); + if (id) { + res = env->CallObjectMethodV(d->m_jobject, id, args); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +QJniObject QJniObject::callStaticObjectMethodV(const char *className, + const char *methodName, + const char *sig, + va_list args) +{ + QJniEnvironment env; + jobject res = nullptr; + jclass clazz = loadClass(className, env); + if (clazz) { + jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, sig, true); + if (id) { + res = env->CallStaticObjectMethodV(clazz, id, args); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +QJniObject QJniObject::callStaticObjectMethodV(jclass clazz, + const char *methodName, + const char *sig, + va_list args) +{ + QJniEnvironment env; + jobject res = nullptr; + jmethodID id = getMethodID(env, clazz, methodName, sig, true); + if (id) { + res = env->CallStaticObjectMethodV(clazz, id, args); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn template <typename T> T QJniObject::callMethod(const char *methodName, const char *sig, ...) const + + Calls the method \a methodName with a signature \a sig and returns the value. + + \code + QJniObject myJavaString = ...; + jint index = myJavaString.callMethod<jint>("indexOf", "(I)I", 0x0051); + \endcode + +*/ +template <> +Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName, const char *sig, ...) const +{ + QJniEnvironment env; + jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig); + if (id) { + va_list args; + va_start(args, sig); + env->CallVoidMethodV(d->m_jobject, id, args); + va_end(args); + env.exceptionCheckAndClear(); + } +} + +/*! + \fn template <typename T> T QJniObject::callMethod(const char *methodName) const + + Calls the method \a methodName and returns the value. + + \code + QJniObject myJavaString = ...; + jint size = myJavaString.callMethod<jint>("length"); + \endcode +*/ +template <> +Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName) const +{ + callMethod<void>(methodName, "()V"); +} + +/*! + \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...) + + Calls the static method with \a methodName with \a signature on class \a className with optional arguments. + + \code + ... + jint a = 2; + jint b = 4; + jint max = QJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b); + ... + \endcode +*/ +template <> +Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className, + const char *methodName, + const char *sig, + ...) +{ + QJniEnvironment env; + jclass clazz = loadClass(className, env); + if (clazz) { + jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), + methodName, sig, true); + if (id) { + va_list args; + va_start(args, sig); + env->CallStaticVoidMethodV(clazz, id, args); + va_end(args); + env.exceptionCheckAndClear(); + } + } +} + +/*! + \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName) + + Calls the static method \a methodName on class \a className and returns the value. + + \code + jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod"); + \endcode +*/ +template <> +Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className, const char *methodName) +{ + callStaticMethod<void>(className, methodName, "()V"); +} + +/*! + \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...) + + Calls the static method \a methodName with \a signature on \a clazz and returns the value. + + \code + ... + jclass javaMathClass = ...; // ("java/lang/Math") + jint a = 2; + jint b = 4; + jint max = QJniObject::callStaticMethod<jint>(javaMathClass, "max", "(II)I", a, b); + ... + \endcode +*/ +template <> +Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz, + const char *methodName, + const char *sig, + ...) +{ + QJniEnvironment env; + if (clazz) { + jmethodID id = getMethodID(env, clazz, methodName, sig, true); + if (id) { + va_list args; + va_start(args, sig); + env->CallStaticVoidMethodV(clazz, id, args); + va_end(args); + env.exceptionCheckAndClear(); + } + } +} + +template <> +Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(const char *className, + const char *methodName, + const char *sig, + va_list args) +{ + QJniEnvironment env; + jclass clazz = loadClass(className, env); + if (clazz) { + jmethodID id = getCachedMethodID(env, clazz, + toBinaryEncClassName(className), methodName, + sig, true); + if (id) { + env->CallStaticVoidMethodV(clazz, id, args); + env.exceptionCheckAndClear(); + } + } +} + +template <> +Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(jclass clazz, + const char *methodName, + const char *sig, + va_list args) +{ + QJniEnvironment env; + jmethodID id = getMethodID(env, clazz, methodName, sig, true); + if (id) { + env->CallStaticVoidMethodV(clazz, id, args); + env.exceptionCheckAndClear(); + } +} + +/*! + \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName) + + Calls the static method \a methodName on \a clazz and returns the value. + + \code + ... + jclass javaMathClass = ...; // ("java/lang/Math") + jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random"); + ... + \endcode +*/ +template <> +Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz, const char *methodName) +{ + callStaticMethod<void>(clazz, methodName, "()V"); +} + +template <> +Q_CORE_EXPORT void QJniObject::callMethodV<void>(const char *methodName, const char *sig, + va_list args) const +{ + QJniEnvironment env; + jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig); + if (id) { + env->CallVoidMethodV(d->m_jobject, id, args); + env.exceptionCheckAndClear(); + } +} + +#define MAKE_JNI_METHODS(MethodName, Type, Signature) \ +template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName, \ + const char *sig, ...) const \ +{ \ + QJniEnvironment env; \ + Type res = 0; \ + jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig); \ + if (id) { \ + va_list args; \ + va_start(args, sig); \ + res = env->Call##MethodName##MethodV(d->m_jobject, id, args); \ + va_end(args); \ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + } \ + return res; \ +}\ +template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName) const \ +{ \ + return callMethod<Type>(methodName, Signature); \ +} \ +\ +template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \ + const char *methodName, \ + const char *sig, \ + ...) \ +{ \ + QJniEnvironment env; \ + Type res = 0; \ + jclass clazz = loadClass(className, env); \ + if (clazz) { \ + jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, \ + sig, true); \ + if (id) { \ + va_list args; \ + va_start(args, sig); \ + res = env->CallStatic##MethodName##MethodV(clazz, id, args); \ + va_end(args); \ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + } \ + } \ + return res; \ +} \ +template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \ + const char *methodName) \ +{ \ + return callStaticMethod<Type>(className, methodName, Signature); \ +}\ +\ +template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \ + const char *methodName, \ + const char *sig, \ + ...) \ +{ \ + QJniEnvironment env; \ + Type res = 0; \ + if (clazz) { \ + jmethodID id = getMethodID(env, clazz, methodName, sig, true); \ + if (id) { \ + va_list args; \ + va_start(args, sig); \ + res = env->CallStatic##MethodName##MethodV(clazz, id, args); \ + va_end(args); \ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + } \ + } \ + return res; \ +} \ +template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \ + const char *methodName) \ +{ \ + return callStaticMethod<Type>(clazz, methodName, Signature); \ +}\ +template <> \ +Q_CORE_EXPORT Type QJniObject::callMethodV<Type>(const char *methodName, const char *sig,\ + va_list args) const\ +{\ + QJniEnvironment env;\ + Type res = 0;\ + jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig);\ + if (id) {\ + res = env->Call##MethodName##MethodV(d->m_jobject, id, args);\ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + }\ + return res;\ +}\ +template <>\ +Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(const char *className,\ + const char *methodName,\ + const char *sig,\ + va_list args)\ +{\ + QJniEnvironment env;\ + Type res = 0;\ + jclass clazz = loadClass(className, env);\ + if (clazz) {\ + jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName,\ + sig, true);\ + if (id) {\ + res = env->CallStatic##MethodName##MethodV(clazz, id, args);\ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + }\ + }\ + return res;\ +}\ +template <>\ +Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(jclass clazz,\ + const char *methodName,\ + const char *sig,\ + va_list args)\ +{\ + QJniEnvironment env;\ + Type res = 0;\ + jmethodID id = getMethodID(env, clazz, methodName, sig, true);\ + if (id) {\ + res = env->CallStatic##MethodName##MethodV(clazz, id, args);\ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + }\ + return res;\ +} + +#define DECLARE_JNI_METHODS(MethodName, Type, Signature) MAKE_JNI_METHODS(MethodName, \ + Type, \ + Signature) +DECLARE_JNI_METHODS(Boolean, jboolean, "()Z") +DECLARE_JNI_METHODS(Byte, jbyte, "()B") +DECLARE_JNI_METHODS(Char, jchar, "()C") +DECLARE_JNI_METHODS(Short, jshort, "()S") +DECLARE_JNI_METHODS(Int, jint, "()I") +DECLARE_JNI_METHODS(Long, jlong, "()J") +DECLARE_JNI_METHODS(Float, jfloat, "()F") +DECLARE_JNI_METHODS(Double, jdouble, "()D") + +/*! + \fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const + + Calls the Java object's method \a methodName with the signature \a signature and arguments + + \code + QJniObject myJavaString; ==> "Hello, Java" + QJniObject mySubstring = myJavaString.callObjectMethod("substring", "(II)Ljava/lang/String;", 7, 10); + \endcode +*/ +QJniObject QJniObject::callObjectMethod(const char *methodName, const char *sig, ...) const +{ + QJniEnvironment env; + jobject res = nullptr; + jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, sig); + if (id) { + va_list args; + va_start(args, sig); + res = env->CallObjectMethodV(d->m_jobject, id, args); + va_end(args); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...) + + Calls the static method with \a methodName and \a signature on the class \a className. + + \code + QJniObject thread = QJniObject::callStaticObjectMethod("java/lang/Thread", "currentThread", "()Ljava/lang/Thread;"); + QJniObject string = QJniObject::callStaticObjectMethod("java/lang/String", "valueOf", "(I)Ljava/lang/String;", 10); + \endcode +*/ +QJniObject QJniObject::callStaticObjectMethod(const char *className, + const char *methodName, + const char *sig, + ...) +{ + QJniEnvironment env; + jobject res = nullptr; + jclass clazz = loadClass(className, env); + if (clazz) { + jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, + sig, true); + if (id) { + va_list args; + va_start(args, sig); + res = env->CallStaticObjectMethodV(clazz, id, args); + va_end(args); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...) + + Calls the static method with \a methodName and \a signature on class \a clazz. +*/ +QJniObject QJniObject::callStaticObjectMethod(jclass clazz, + const char *methodName, + const char *sig, + ...) +{ + QJniEnvironment env; + jobject res = nullptr; + if (clazz) { + jmethodID id = getMethodID(env, clazz, methodName, sig, true); + if (id) { + va_list args; + va_start(args, sig); + res = env->CallStaticObjectMethodV(clazz, id, args); + va_end(args); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + } + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn QJniObject QJniObject::callObjectMethod(const char *methodName) const + + Calls the Java objects method \a methodName and returns a new QJniObject for + the returned Java object. + + \code + ... + QJniObject myJavaString1 = ...; + QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString"); + ... + \endcode +*/ + +/*! + \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName) + + Calls the static method with \a methodName on the class \a className. + + \code + QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName"); + \endcode +*/ + +/*! + \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName) + + Calls the static method with \a methodName on \a clazz. + +*/ + +/*! + \fn template <typename T> QJniObject &QJniObject::operator=(T object) + + Replace the current object with \a object. The old Java object will be released. +*/ +#define MAKE_JNI_OBJECT_METHODS(Type, Signature) \ +template <> \ +Q_CORE_EXPORT QJniObject QJniObject::callObjectMethod<Type>(const char *methodName) const \ +{ \ + return callObjectMethod(methodName, Signature); \ +} \ +template <> \ +Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(const char *className, \ + const char *methodName) \ +{ \ + return callStaticObjectMethod(className, methodName, Signature); \ +} \ +template <> \ +Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(jclass clazz, \ + const char *methodName) \ +{ \ + return callStaticObjectMethod(clazz, methodName, Signature); \ +}\ +template <>\ +Q_CORE_EXPORT Type QJniObject::object<Type>() const\ +{\ + return static_cast<Type>(javaObject());\ +}\ +template <>\ +Q_CORE_EXPORT QJniObject &QJniObject::operator=(Type obj)\ +{\ + assign(static_cast<jobject>(obj));\ + return *this;\ +} + +#define DECLARE_JNI_OBJECT_METHODS(Type, Signature) MAKE_JNI_OBJECT_METHODS(Type, Signature) + +DECLARE_JNI_OBJECT_METHODS(jobject, "()Ljava/lang/Object;") +DECLARE_JNI_OBJECT_METHODS(jclass, "()Ljava/lang/Class;") +DECLARE_JNI_OBJECT_METHODS(jstring, "()Ljava/lang/String;") +DECLARE_JNI_OBJECT_METHODS(jobjectArray, "()[Ljava/lang/Object;") +DECLARE_JNI_OBJECT_METHODS(jbooleanArray, "()[Z") +DECLARE_JNI_OBJECT_METHODS(jbyteArray, "()[B") +DECLARE_JNI_OBJECT_METHODS(jshortArray, "()[S") +DECLARE_JNI_OBJECT_METHODS(jintArray, "()[I") +DECLARE_JNI_OBJECT_METHODS(jlongArray, "()[J") +DECLARE_JNI_OBJECT_METHODS(jfloatArray, "()[F") +DECLARE_JNI_OBJECT_METHODS(jdoubleArray, "()[D") +DECLARE_JNI_OBJECT_METHODS(jcharArray, "()[C") +DECLARE_JNI_OBJECT_METHODS(jthrowable, "()Ljava/lang/Throwable;") + +/*! + \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, T value); + + Sets the static field with \a fieldName and \a signature to \a value on class named \a className. +*/ +template <> +Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(const char *className, + const char *fieldName, + const char *sig, + jobject value) +{ + QJniEnvironment env; + jclass clazz = loadClass(className, env); + + if (!clazz) + return; + + jfieldID id = getCachedFieldID(env, clazz, className, fieldName, sig, true); + if (id) { + env->SetStaticObjectField(clazz, id, value); + env.exceptionCheckAndClear(); + } +} + +/*! + \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, T value); + + Sets the static field with \a fieldName and \a signature to \a value on class \a clazz. +*/ +template <> Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(jclass clazz, + const char *fieldName, + const char *sig, + jobject value) +{ + QJniEnvironment env; + jfieldID id = getFieldID(env, clazz, fieldName, sig, true); + + if (id) { + env->SetStaticObjectField(clazz, id, value); + env.exceptionCheckAndClear(); + } +} + +/*! + \fn T QJniObject::getField(const char *fieldName) const + + Retrieves the value of the field \a fieldName. + + \code + QJniObject volumeControl = ...; + jint fieldValue = volumeControl.getField<jint>("MAX_VOLUME"); + \endcode +*/ + +/*! + \fn T QJniObject::getStaticField(const char *className, const char *fieldName) + + Retrieves the value from the static field \a fieldName on the class \a className. +*/ + +/*! + \fn T QJniObject::getStaticField(jclass clazz, const char *fieldName) + + Retrieves the value from the static field \a fieldName on \a clazz. +*/ + +/*! + \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, T value) + + Sets the static field \a fieldName of the class named \a className to \a value. +*/ + +/*! + \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, T value) + + Sets the static field \a fieldName of the class \a clazz to \a value. +*/ +#define MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) \ +template <> Q_CORE_EXPORT Type QJniObject::getField<Type>(const char *fieldName) const \ +{ \ + QJniEnvironment env; \ + Type res = 0; \ + jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \ + if (id) {\ + res = env->Get##FieldName##Field(d->m_jobject, id); \ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + } \ + return res;\ +} \ +template <> \ +Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(const char *className, const char *fieldName) \ +{ \ + QJniEnvironment env; \ + jclass clazz = loadClass(className, env); \ + if (!clazz) \ + return 0; \ + jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName, \ + Signature, true); \ + if (!id) \ + return 0; \ + Type res = env->GetStatic##FieldName##Field(clazz, id); \ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + return res;\ +} \ +template <>\ +Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(jclass clazz, const char *fieldName)\ +{\ + QJniEnvironment env;\ + Type res = 0;\ + jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\ + if (id) {\ + res = env->GetStatic##FieldName##Field(clazz, id);\ + if (env.exceptionCheckAndClear()) \ + res = 0; \ + }\ + return res;\ +}\ +template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \ + const char *fieldName, \ + Type value) \ +{ \ + QJniEnvironment env; \ + jclass clazz = loadClass(className, env); \ + if (!clazz) \ + return; \ + jfieldID id = getCachedFieldID(env, clazz, className, fieldName, Signature, true); \ + if (!id) \ + return; \ + env->SetStatic##FieldName##Field(clazz, id, value); \ + env.exceptionCheckAndClear(); \ +}\ +template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(jclass clazz,\ + const char *fieldName,\ + Type value)\ +{\ + QJniEnvironment env;\ + jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\ + if (id) {\ + env->SetStatic##FieldName##Field(clazz, id, value);\ + env.exceptionCheckAndClear();\ + }\ +}\ +template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \ +{ \ + QJniEnvironment env; \ + jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \ + if (id) { \ + env->Set##FieldName##Field(d->m_jobject, id, value); \ + env.exceptionCheckAndClear(); \ + } \ +} \ + +#define DECLARE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, \ + Signature) +DECLARE_JNI_PRIMITIVE_FIELDS(Boolean, jboolean, "Z") +DECLARE_JNI_PRIMITIVE_FIELDS(Byte, jbyte, "B") +DECLARE_JNI_PRIMITIVE_FIELDS(Char, jchar, "C") +DECLARE_JNI_PRIMITIVE_FIELDS(Short, jshort, "S") +DECLARE_JNI_PRIMITIVE_FIELDS(Int, jint, "I") +DECLARE_JNI_PRIMITIVE_FIELDS(Long, jlong, "J") +DECLARE_JNI_PRIMITIVE_FIELDS(Float, jfloat, "F") +DECLARE_JNI_PRIMITIVE_FIELDS(Double, jdouble, "D") + +/*! + \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature) + Retrieves the object from the field with \a signature and \a fieldName on class \a className. + + \note This function can be used without a template type. + + \code + QJniObject jobj = QJniObject::getStaticObjectField("class/with/Fields", "FIELD_NAME", "Ljava/lang/String;"); + \endcode +*/ +QJniObject QJniObject::getStaticObjectField(const char *className, + const char *fieldName, + const char *sig) +{ + QJniEnvironment env; + jclass clazz = loadClass(className, env); + if (!clazz) + return QJniObject(); + jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName, + sig, true); + if (!id) + return QJniObject(); + + jobject res = env->GetStaticObjectField(clazz, id); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName, const char *signature) + Retrieves the object from the field with \a signature and \a fieldName on \a clazz. + + \note This function can be used without a template type. + + \code + QJniObject jobj = QJniObject::getStaticObjectField(clazz, "FIELD_NAME", "Ljava/lang/String;"); + \endcode +*/ +QJniObject QJniObject::getStaticObjectField(jclass clazz, + const char *fieldName, + const char *sig) +{ + QJniEnvironment env; + jobject res = nullptr; + jfieldID id = getFieldID(env, clazz, fieldName, sig, true); + if (id) { + res = env->GetStaticObjectField(clazz, id); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, const char *fieldName, const char *signature) + + Retrieves the \a jobject from the field with \a signature and \a fieldName on \a clazz. +*/ +template <> +Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, + const char *fieldName, + const char *sig) +{ + return getStaticObjectField(clazz, fieldName, sig); +} + +/*! + \fn QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, const char *fieldName, const char *signature) + + Retrieves the jobject from the field with \a signature and \a fieldName on class named \a className. +*/ +template <> +Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(const char *className, + const char *fieldName, + const char *sig) +{ + return getStaticObjectField(className, fieldName, sig); +} + +/*! + \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, const char *fieldName, const char *signature) + + Retrieves the jobjectArray from the field with \a signature and \a fieldName on \a clazz. +*/ +template <> +Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, + const char *fieldName, + const char *sig) +{ + return getStaticObjectField(clazz, fieldName, sig); +} + +/*! + \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, const char *fieldName, const char *signature) + + Retrieves the jobjectArray from the field with \a signature and \a fieldName on the class named \a className. +*/ +template <> +Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(const char *className, + const char *fieldName, + const char *sig) +{ + return getStaticObjectField(className, fieldName, sig); +} + +/*! + \fn template <typename T> void QJniObject::setField(const char *fieldName, const char *signature, T value) + + Sets the value of \a fieldName with \a signature to \a value. + + \code + QJniObject stringArray = ...; + QJniObject obj = ...; + obj.setField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V", stringArray.object<jobjectArray>()) + \endcode +*/ +template <> Q_CORE_EXPORT +void QJniObject::setField<jobject>(const char *fieldName, const char *sig, jobject value) +{ + QJniEnvironment env; + jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, sig); + if (id) { + env->SetObjectField(d->m_jobject, id, value); + env.exceptionCheckAndClear(); + } +} + +template <> Q_CORE_EXPORT +void QJniObject::setField<jobjectArray>(const char *fieldName, const char *sig, jobjectArray value) +{ + QJniEnvironment env; + jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, sig); + if (id) { + env->SetObjectField(d->m_jobject, id, value); + env.exceptionCheckAndClear(); + } +} + +/*! + \fn QJniObject QJniObject::getObjectField(const char *fieldName) const + + Retrieves the object of field \a fieldName. + + \code + QJniObject field = jniObject.getObjectField<jstring>("FIELD_NAME"); + \endcode +*/ + +/*! + \fn QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const + + Retrieves the object from the field with \a signature and \a fieldName. + + \note This function can be used without a template type. + + \code + QJniObject field = jniObject.getObjectField("FIELD_NAME", "Ljava/lang/String;"); + \endcode +*/ +QJniObject QJniObject::getObjectField(const char *fieldName, const char *sig) const +{ + QJniEnvironment env; + jobject res = nullptr; + jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, sig); + if (id) { + res = env->GetObjectField(d->m_jobject, id); + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn template <typename T> void QJniObject::setField(const char *fieldName, T value) + + Sets the value of \a fieldName to \a value. + + \code + ... + QJniObject obj; + obj.setField<jint>("AN_INT_FIELD", 10); + jstring myString = ... + obj.setField<jstring>("A_STRING_FIELD", myString); + ... + \endcode +*/ + +/*! + \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName) + + Retrieves the object from the field \a fieldName on the class \a className. + + \code + QJniObject jobj = QJniObject::getStaticObjectField<jstring>("class/with/Fields", "FIELD_NAME"); + \endcode +*/ + +/*! + \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName) + + Retrieves the object from the field \a fieldName on \a clazz. + + \code + QJniObject jobj = QJniObject::getStaticObjectField<jstring>(clazz, "FIELD_NAME"); + \endcode +*/ + +#define MAKE_JNI_OBJECT_FILEDS(Type, Signature) \ +template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \ +{ \ + QJniObject::setField<jobject>(fieldName, Signature, value); \ +} \ +\ +template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \ + const char *fieldName, \ + Type value) \ +{ \ + QJniObject::setStaticField<jobject>(className, fieldName, Signature, value); \ +}\ +template <>\ +Q_CORE_EXPORT QJniObject QJniObject::getObjectField<Type>(const char *fieldName) const\ +{\ + return getObjectField(fieldName, Signature);\ +}\ +template <>\ +Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(jclass clazz,\ + const char *fieldName)\ +{\ + return getStaticObjectField(clazz, fieldName, Signature);\ +}\ +template <>\ +Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(const char *className,\ + const char *fieldName)\ +{\ + return getStaticObjectField(className, fieldName, Signature);\ +}\ + +#define DECLARE_JNI_OBJECT_FILEDS(Type, Signature) MAKE_JNI_OBJECT_FILEDS(Type, Signature) + +DECLARE_JNI_OBJECT_FILEDS(jobject, "Ljava/lang/Object;") +DECLARE_JNI_OBJECT_FILEDS(jobjectArray, "[Ljava/lang/Object;") +DECLARE_JNI_OBJECT_FILEDS(jstring, "Ljava/lang/String;") +DECLARE_JNI_OBJECT_FILEDS(jclass, "Ljava/lang/Class;") +DECLARE_JNI_OBJECT_FILEDS(jthrowable, "Ljava/lang/Throwable;") +DECLARE_JNI_OBJECT_FILEDS(jbooleanArray, "[Z") +DECLARE_JNI_OBJECT_FILEDS(jbyteArray, "[B") +DECLARE_JNI_OBJECT_FILEDS(jcharArray, "[C") +DECLARE_JNI_OBJECT_FILEDS(jshortArray, "[S") +DECLARE_JNI_OBJECT_FILEDS(jintArray, "[I") +DECLARE_JNI_OBJECT_FILEDS(jlongArray, "[J") +DECLARE_JNI_OBJECT_FILEDS(jfloatArray, "[F") +DECLARE_JNI_OBJECT_FILEDS(jdoubleArray, "[D") + +/*! + \fn QJniObject QJniObject::fromString(const QString &string) + + Creates a Java string from the QString \a string and returns a QJniObject holding that string. + + \code + QString myQString = "QString"; + QJniObject myJavaString = QJniObject::fromString(myQString); + \endcode + + \sa toString() +*/ +QJniObject QJniObject::fromString(const QString &string) +{ + QJniEnvironment env; + jstring res = env->NewString(reinterpret_cast<const jchar*>(string.constData()), + string.length()); + + if (env.exceptionCheckAndClear()) { + env->DeleteLocalRef(res); + res = nullptr; + } + + QJniObject obj(res); + env->DeleteLocalRef(res); + return obj; +} + +/*! + \fn QString QJniObject::toString() const + + Returns a QString with a string representation of the java object. + Calling this function on a Java String object is a convenient way of getting the actual string + data. + + \code + QJniObject string = ...; // "Hello Java" + QString qstring = string.toString(); // "Hello Java" + \endcode + + \sa fromString() +*/ +QString QJniObject::toString() const +{ + if (!isValid()) + return QString(); + + QJniObject string = callObjectMethod<jstring>("toString"); + return qt_convertJString(static_cast<jstring>(string.object())); +} + +/*! + \fn bool QJniObject::isClassAvailable(const char *className) + + Returns true if the Java class \a className is available. + + \code + if (QJniObject::isClassAvailable("java/lang/String")) { + // condition statement + } + \endcode +*/ +bool QJniObject::isClassAvailable(const char *className) +{ + QJniEnvironment env; + + if (!env) + return false; + + return loadClass(className, env);; +} + +/*! + \fn bool QJniObject::isValid() const + + Returns true if this instance holds a valid Java object. + + \code + QJniObject qjniObject; ==> isValid() == false + QJniObject qjniObject(0) ==> isValid() == false + QJniObject qjniObject("could/not/find/Class") ==> isValid() == false + \endcode +*/ +bool QJniObject::isValid() const +{ + return d->m_jobject; +} + +/*! + \fn QJniObject QJniObject::fromLocalRef(jobject localRef) + \since 6.1 + + Creates a QJniObject from the local JNI reference \a localRef. + This function takes ownership of \a localRef and frees it before returning. + + \note Only call this function with a local JNI reference. For example, most raw JNI calls, + through the JNI environment, return local references to a java object. + + \code + jobject localRef = env->GetObjectArrayElement(array, index); + QJniObject element = QJniObject::fromLocalRef(localRef); + \endcode +*/ +QJniObject QJniObject::fromLocalRef(jobject lref) +{ + QJniObject obj(lref); + QJniEnvironment()->DeleteLocalRef(lref); + return obj; +} + +bool QJniObject::isSameObject(jobject obj) const +{ + return QJniEnvironment()->IsSameObject(d->m_jobject, obj); +} + +bool QJniObject::isSameObject(const QJniObject &other) const +{ + return isSameObject(other.d->m_jobject); +} + +void QJniObject::assign(jobject obj) +{ + if (isSameObject(obj)) + return; + + jobject jobj = static_cast<jobject>(obj); + d = QSharedPointer<QJniObjectPrivate>::create(); + if (obj) { + QJniEnvironment env; + d->m_jobject = env->NewGlobalRef(jobj); + jclass objectClass = env->GetObjectClass(jobj); + d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass)); + env->DeleteLocalRef(objectClass); + } +} + +jobject QJniObject::javaObject() const +{ + return d->m_jobject; +} + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h new file mode 100644 index 0000000000..2998c7b4e8 --- /dev/null +++ b/src/corelib/kernel/qjniobject.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module 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$ +** +****************************************************************************/ + +#ifndef QJNIOBJECT_H +#define QJNIOBJECT_H + +#include <QtCore/QSharedPointer> + +#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) +#include <jni.h> +#else +class jclass; +class jobject; +#endif + +QT_BEGIN_NAMESPACE + +class QJniObjectPrivate; + +class Q_CORE_EXPORT QJniObject +{ +public: + QJniObject(); + explicit QJniObject(const char *className); + explicit QJniObject(const char *className, const char *sig, ...); + explicit QJniObject(jclass clazz); + explicit QJniObject(jclass clazz, const char *sig, ...); + QJniObject(jobject globalRef); + ~QJniObject(); + + template <typename T> + T object() const; + jobject object() const; + + template <typename T> + T callMethod(const char *methodName, const char *sig, ...) const; + template <typename T> + T callMethod(const char *methodName) const; + template <typename T> + QJniObject callObjectMethod(const char *methodName) const; + QJniObject callObjectMethod(const char *methodName, const char *sig, ...) const; + + template <typename T> + static T callStaticMethod(const char *className, const char *methodName, const char *sig, ...); + template <typename T> + static T callStaticMethod(const char *className, const char *methodName); + template <typename T> + static T callStaticMethod(jclass clazz, const char *methodName, const char *sig, ...); + template <typename T> + static T callStaticMethod(jclass clazz, const char *methodName); + + template <typename T> + static QJniObject callStaticObjectMethod(const char *className, const char *methodName); + static QJniObject callStaticObjectMethod(const char *className, + const char *methodName, + const char *sig, ...); + + template <typename T> + static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName); + static QJniObject callStaticObjectMethod(jclass clazz, + const char *methodName, + const char *sig, ...); + + template <typename T> + T getField(const char *fieldName) const; + + template <typename T> + static T getStaticField(const char *className, const char *fieldName); + template <typename T> + static T getStaticField(jclass clazz, const char *fieldName); + + template <typename T> + QJniObject getObjectField(const char *fieldName) const; + QJniObject getObjectField(const char *fieldName, const char *sig) const; + + template <typename T> + static QJniObject getStaticObjectField(const char *className, const char *fieldName); + static QJniObject getStaticObjectField(const char *className, + const char *fieldName, + const char *sig); + template <typename T> + static QJniObject getStaticObjectField(const char *className, + const char *fieldName, + const char *sig); + + template <typename T> + static QJniObject getStaticObjectField(jclass clazz, const char *fieldName); + static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *sig); + template <typename T> + static QJniObject getStaticObjectField(jclass clazz, const char *fieldName, const char *sig); + + template <typename T> + void setField(const char *fieldName, T value); + template <typename T> + void setField(const char *fieldName, const char *sig, T value); + template <typename T> + static void setStaticField(const char *className, const char *fieldName, T value); + template <typename T> + static void setStaticField(const char *className, const char *fieldName, + const char *sig, T value); + template <typename T> + static void setStaticField(jclass clazz, const char *fieldName, const char *sig, T value); + + template <typename T> + static void setStaticField(jclass clazz, const char *fieldName, T value); + + static QJniObject fromString(const QString &string); + QString toString() const; + + static bool isClassAvailable(const char *className); + bool isValid() const; + + // This function takes ownership of the jobject and releases the local ref. before returning. + static QJniObject fromLocalRef(jobject lref); + + template <typename T> QJniObject &operator=(T obj); + +private: + struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; }; + + QJniObject(const char *className, const char *sig, const QVaListPrivate &args); + QJniObject(jclass clazz, const char *sig, const QVaListPrivate &args); + + template <typename T> + T callMethodV(const char *methodName, const char *sig, va_list args) const; + QJniObject callObjectMethodV(const char *methodName, const char *sig, va_list args) const; + template <typename T> + static T callStaticMethodV(const char *className, + const char *methodName, + const char *sig, + va_list args); + template <typename T> + static T callStaticMethodV(jclass clazz, + const char *methodName, + const char *sig, + va_list args); + + static QJniObject callStaticObjectMethodV(const char *className, + const char *methodName, + const char *sig, + va_list args); + + static QJniObject callStaticObjectMethodV(jclass clazz, + const char *methodName, + const char *sig, + va_list args); + + bool isSameObject(jobject obj) const; + bool isSameObject(const QJniObject &other) const; + void assign(jobject obj); + jobject javaObject() const; + + friend bool operator==(const QJniObject &, const QJniObject &); + friend bool operator!=(const QJniObject&, const QJniObject&); + + QSharedPointer<QJniObjectPrivate> d; +}; + +inline bool operator==(const QJniObject &obj1, const QJniObject &obj2) +{ + return obj1.isSameObject(obj2); +} + +inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2) +{ + return !obj1.isSameObject(obj2); +} + +QT_END_NAMESPACE + +#endif // QJNIOBJECT_H diff --git a/src/corelib/kernel/qjnionload.cpp b/src/corelib/kernel/qjnionload.cpp index ec56d3de66..dbaee48ac6 100644 --- a/src/corelib/kernel/qjnionload.cpp +++ b/src/corelib/kernel/qjnionload.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -37,8 +37,9 @@ ** ****************************************************************************/ -#include <jni.h> #include "qjnihelpers_p.h" + +#include <jni.h> #include <android/log.h> static const char logTag[] = "QtCore"; diff --git a/src/corelib/time/qtimezoneprivate_android.cpp b/src/corelib/time/qtimezoneprivate_android.cpp index da82832455..20f7f2df68 100644 --- a/src/corelib/time/qtimezoneprivate_android.cpp +++ b/src/corelib/time/qtimezoneprivate_android.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Copyright (C) 2014 Drew Parsons <dparsons@emerall.com> ** Contact: https://www.qt.io/licensing/ ** @@ -38,10 +38,12 @@ ** ****************************************************************************/ -#include <QtCore/QSet> #include "qtimezone.h" #include "qtimezoneprivate_p.h" +#include <QtCore/QJniEnvironment> +#include <QtCore/QSet> + QT_BEGIN_NAMESPACE /* @@ -49,7 +51,7 @@ QT_BEGIN_NAMESPACE Android implementation - Note that a QJNIObjectPrivate manages a global reference, so it serves as an + Note that a QJniObject manages a global reference, so it serves as an owning smart-pointer, ensuring an object doesn't get garbage-collected before we're done with it. */ @@ -59,9 +61,9 @@ QAndroidTimeZonePrivate::QAndroidTimeZonePrivate() : QTimeZonePrivate() { // Keep in sync with systemTimeZoneId(): - androidTimeZone = QJNIObjectPrivate::callStaticObjectMethod( + androidTimeZone = QJniObject::callStaticObjectMethod( "java.util.TimeZone", "getDefault", "()Ljava/util/TimeZone;"); - const QJNIObjectPrivate id = androidTimeZone.callObjectMethod("getID", "()Ljava/lang/String;"); + const QJniObject id = androidTimeZone.callObjectMethod("getID", "()Ljava/lang/String;"); m_id = id.toString().toUtf8(); } @@ -83,16 +85,16 @@ QAndroidTimeZonePrivate::~QAndroidTimeZonePrivate() { } -static QJNIObjectPrivate getDisplayName(QJNIObjectPrivate zone, jint style, jboolean dst, +static QJniObject getDisplayName(QJniObject zone, jint style, jboolean dst, const QLocale &locale) { - QJNIObjectPrivate jlanguage - = QJNIObjectPrivate::fromString(QLocale::languageToString(locale.language())); - QJNIObjectPrivate jcountry - = QJNIObjectPrivate::fromString(QLocale::countryToString(locale.country())); - QJNIObjectPrivate - jvariant = QJNIObjectPrivate::fromString(QLocale::scriptToString(locale.script())); - QJNIObjectPrivate jlocale("java.util.Locale", + QJniObject jlanguage + = QJniObject::fromString(QLocale::languageToString(locale.language())); + QJniObject jcountry + = QJniObject::fromString(QLocale::countryToString(locale.country())); + QJniObject + jvariant = QJniObject::fromString(QLocale::scriptToString(locale.script())); + QJniObject jlocale("java.util.Locale", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", static_cast<jstring>(jlanguage.object()), static_cast<jstring>(jcountry.object()), @@ -106,12 +108,12 @@ static QJNIObjectPrivate getDisplayName(QJNIObjectPrivate zone, jint style, jboo void QAndroidTimeZonePrivate::init(const QByteArray &ianaId) { const QString iana = QString::fromUtf8(ianaId); - androidTimeZone = QJNIObjectPrivate::callStaticObjectMethod( + androidTimeZone = QJniObject::callStaticObjectMethod( "java.util.TimeZone", "getTimeZone", "(Ljava/lang/String;)Ljava/util/TimeZone;", - static_cast<jstring>(QJNIObjectPrivate::fromString(iana).object())); + static_cast<jstring>(QJniObject::fromString(iana).object())); // The ID or display name of the zone we've got, if it looks like what we asked for: - const auto match = [iana](const QJNIObjectPrivate &jname) -> QByteArray { + const auto match = [iana](const QJniObject &jname) -> QByteArray { const QString name = jname.toString(); if (iana.compare(name, Qt::CaseInsensitive) == 0) return name.toUtf8(); @@ -205,7 +207,7 @@ bool QAndroidTimeZonePrivate::hasDaylightTime() const bool QAndroidTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const { if ( androidTimeZone.isValid() ) { - QJNIObjectPrivate jDate( "java/util/Date", "(J)V", static_cast<jlong>(atMSecsSinceEpoch) ); + QJniObject jDate( "java/util/Date", "(J)V", static_cast<jlong>(atMSecsSinceEpoch) ); return androidTimeZone.callMethod<jboolean>("inDaylightTime", "(Ljava/util/Date;)Z", jDate.object() ); } else @@ -250,24 +252,24 @@ QTimeZonePrivate::Data QAndroidTimeZonePrivate::previousTransition(qint64 before QByteArray QAndroidTimeZonePrivate::systemTimeZoneId() const { // Keep in sync with default constructor: - QJNIObjectPrivate androidSystemTimeZone = QJNIObjectPrivate::callStaticObjectMethod( + QJniObject androidSystemTimeZone = QJniObject::callStaticObjectMethod( "java.util.TimeZone", "getDefault", "()Ljava/util/TimeZone;"); - const QJNIObjectPrivate id = androidSystemTimeZone.callObjectMethod<jstring>("getID"); + const QJniObject id = androidSystemTimeZone.callObjectMethod<jstring>("getID"); return id.toString().toUtf8(); } QList<QByteArray> QAndroidTimeZonePrivate::availableTimeZoneIds() const { QList<QByteArray> availableTimeZoneIdList; - QJNIObjectPrivate androidAvailableIdList = QJNIObjectPrivate::callStaticObjectMethod("java.util.TimeZone", "getAvailableIDs", "()[Ljava/lang/String;"); + QJniObject androidAvailableIdList = QJniObject::callStaticObjectMethod("java.util.TimeZone", "getAvailableIDs", "()[Ljava/lang/String;"); - QJNIEnvironmentPrivate jniEnv; + QJniEnvironment jniEnv; int androidTZcount = jniEnv->GetArrayLength( static_cast<jarray>(androidAvailableIdList.object()) ); - // need separate jobject and QAndroidJniObject here so that we can delete (DeleteLocalRef) the reference to the jobject + // need separate jobject and QJniObject here so that we can delete (DeleteLocalRef) the reference to the jobject // (or else the JNI reference table fills after 512 entries from GetObjectArrayElement) jobject androidTZobject; - QJNIObjectPrivate androidTZ; + QJniObject androidTZ; for (int i=0; i<androidTZcount; i++ ) { androidTZobject = jniEnv->GetObjectArrayElement( static_cast<jobjectArray>( androidAvailableIdList.object() ), i ); androidTZ = androidTZobject; diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h index d15d7ccfea..b6ae3764b1 100644 --- a/src/corelib/time/qtimezoneprivate_p.h +++ b/src/corelib/time/qtimezoneprivate_p.h @@ -70,7 +70,7 @@ Q_FORWARD_DECLARE_OBJC_CLASS(NSTimeZone); #endif // Q_OS_WIN #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) -#include <QtCore/private/qjni_p.h> +#include <QJniObject> #endif QT_BEGIN_NAMESPACE @@ -496,7 +496,7 @@ public: private: void init(const QByteArray &zoneId); - QJNIObjectPrivate androidTimeZone; + QJniObject androidTimeZone; }; #endif // Q_OS_ANDROID diff --git a/src/network/kernel/qnetworkproxy_android.cpp b/src/network/kernel/qnetworkproxy_android.cpp index 1a6fe2b70e..a09593bf3c 100644 --- a/src/network/kernel/qnetworkproxy_android.cpp +++ b/src/network/kernel/qnetworkproxy_android.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -38,7 +38,9 @@ ****************************************************************************/ #include "qnetworkproxy.h" -#include <QtCore/private/qjni_p.h> + +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> #include <QtCore/private/qjnihelpers_p.h> #ifndef QT_NO_NETWORKPROXY @@ -58,18 +60,18 @@ static const char networkClass[] = "org/qtproject/qt/android/network/QtNetwork"; ProxyInfoObject::ProxyInfoObject() { - QJNIObjectPrivate::callStaticMethod<void>(networkClass, - "registerReceiver", - "(Landroid/content/Context;)V", - QtAndroidPrivate::context()); + QJniObject::callStaticMethod<void>(networkClass, + "registerReceiver", + "(Landroid/content/Context;)V", + QtAndroidPrivate::context()); } ProxyInfoObject::~ProxyInfoObject() { - QJNIObjectPrivate::callStaticMethod<void>(networkClass, - "unregisterReceiver", - "(Landroid/content/Context;)V", - QtAndroidPrivate::context()); + QJniObject::callStaticMethod<void>(networkClass, + "unregisterReceiver", + "(Landroid/content/Context;)V", + QtAndroidPrivate::context()); } QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query) @@ -78,18 +80,18 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro if (!proxyInfoInstance) return proxyList; - QJNIObjectPrivate proxyInfo = QJNIObjectPrivate::callStaticObjectMethod(networkClass, + QJniObject proxyInfo = QJniObject::callStaticObjectMethod(networkClass, "getProxyInfo", "(Landroid/content/Context;)Landroid/net/ProxyInfo;", QtAndroidPrivate::context()); if (proxyInfo.isValid()) { - QJNIObjectPrivate exclusionList = proxyInfo.callObjectMethod("getExclusionList", - "()[Ljava/lang/String;"); + QJniObject exclusionList = proxyInfo.callObjectMethod("getExclusionList", + "()[Ljava/lang/String;"); bool exclude = false; if (exclusionList.isValid()) { jobjectArray listObject = static_cast<jobjectArray>(exclusionList.object()); - QJNIEnvironmentPrivate env; - QJNIObjectPrivate entry; + QJniEnvironment env; + QJniObject entry; const int size = env->GetArrayLength(listObject); QUrl host = QUrl(query.url().host()); for (int i = 0; i < size; ++i) { @@ -101,7 +103,7 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro } } if (!exclude) { - QJNIObjectPrivate hostName = proxyInfo.callObjectMethod<jstring>("getHost"); + QJniObject hostName = proxyInfo.callObjectMethod<jstring>("getHost"); const int port = proxyInfo.callMethod<jint>("getPort"); QNetworkProxy proxy(QNetworkProxy::HttpProxy, hostName.toString(), port); proxyList << proxy; diff --git a/src/network/ssl/qsslsocket_openssl_android.cpp b/src/network/ssl/qsslsocket_openssl_android.cpp index deec1532a2..8d0c9a69ee 100644 --- a/src/network/ssl/qsslsocket_openssl_android.cpp +++ b/src/network/ssl/qsslsocket_openssl_android.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtNetwork module of the Qt Toolkit. @@ -53,7 +53,8 @@ ****************************************************************************/ #include "qsslsocket_openssl_p.h" -#include <QtCore/private/qjni_p.h> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> QT_BEGIN_NAMESPACE @@ -61,13 +62,13 @@ QList<QByteArray> QSslSocketPrivate::fetchSslCertificateData() { QList<QByteArray> certificateData; - QJNIObjectPrivate certificates = QJNIObjectPrivate::callStaticObjectMethod("org/qtproject/qt/android/QtNative", - "getSSLCertificates", - "()[[B"); + QJniObject certificates = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative", + "getSSLCertificates", + "()[[B"); if (!certificates.isValid()) return certificateData; - QJNIEnvironmentPrivate env; + QJniEnvironment env; jobjectArray jcertificates = static_cast<jobjectArray>(certificates.object()); const jint nCertificates = env->GetArrayLength(jcertificates); certificateData.reserve(static_cast<int>(nCertificates)); diff --git a/src/plugins/platforms/android/androidcontentfileengine.cpp b/src/plugins/platforms/android/androidcontentfileengine.cpp index 749ce136b6..149bc1139d 100644 --- a/src/plugins/platforms/android/androidcontentfileengine.cpp +++ b/src/plugins/platforms/android/androidcontentfileengine.cpp @@ -39,7 +39,8 @@ #include "androidcontentfileengine.h" -#include <private/qjni_p.h> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> #include <private/qjnihelpers_p.h> #include <QDebug> @@ -65,12 +66,12 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode) openModeStr += QLatin1Char('a'); } - const auto fd = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt/android/QtNative", + const auto fd = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative", "openFdForContentUrl", "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)I", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(fileName(DefaultName)).object(), - QJNIObjectPrivate::fromString(openModeStr).object()); + QJniObject::fromString(fileName(DefaultName)).object(), + QJniObject::fromString(openModeStr).object()); if (fd < 0) { return false; @@ -81,10 +82,10 @@ bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode) qint64 AndroidContentFileEngine::size() const { - const jlong size = QJNIObjectPrivate::callStaticMethod<jlong>( + const jlong size = QJniObject::callStaticMethod<jlong>( "org/qtproject/qt/android/QtNative", "getSize", "(Landroid/content/Context;Ljava/lang/String;)J", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(fileName(DefaultName)).object()); + QJniObject::fromString(fileName(DefaultName)).object()); return (qint64)size; } @@ -92,25 +93,25 @@ AndroidContentFileEngine::FileFlags AndroidContentFileEngine::fileFlags(FileFlag { FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag); FileFlags flags; - const bool isDir = QJNIObjectPrivate::callStaticMethod<jboolean>( + const bool isDir = QJniObject::callStaticMethod<jboolean>( "org/qtproject/qt/android/QtNative", "checkIfDir", "(Landroid/content/Context;Ljava/lang/String;)Z", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(fileName(DefaultName)).object()); + QJniObject::fromString(fileName(DefaultName)).object()); // If it is a directory then we know it exists so there is no reason to explicitly check - const bool exists = isDir ? true : QJNIObjectPrivate::callStaticMethod<jboolean>( + const bool exists = isDir ? true : QJniObject::callStaticMethod<jboolean>( "org/qtproject/qt/android/QtNative", "checkFileExists", "(Landroid/content/Context;Ljava/lang/String;)Z", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(fileName(DefaultName)).object()); + QJniObject::fromString(fileName(DefaultName)).object()); if (!exists && !isDir) return flags; if (isDir) { flags = DirectoryType | commonFlags; } else { flags = FileType | commonFlags; - const bool writable = QJNIObjectPrivate::callStaticMethod<jboolean>( + const bool writable = QJniObject::callStaticMethod<jboolean>( "org/qtproject/qt/android/QtNative", "checkIfWritable", "(Landroid/content/Context;Ljava/lang/String;)Z", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(fileName(DefaultName)).object()); + QJniObject::fromString(fileName(DefaultName)).object()); if (writable) flags |= WriteOwnerPerm|WriteUserPerm|WriteGroupPerm|WriteOtherPerm; } @@ -182,22 +183,22 @@ bool AndroidContentFileEngineIterator::hasNext() const if (m_index == -1) { if (path().isEmpty()) return false; - const bool isDir = QJNIObjectPrivate::callStaticMethod<jboolean>( + const bool isDir = QJniObject::callStaticMethod<jboolean>( "org/qtproject/qt/android/QtNative", "checkIfDir", "(Landroid/content/Context;Ljava/lang/String;)Z", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(path()).object()); + QJniObject::fromString(path()).object()); if (isDir) { - QJNIObjectPrivate objArray = QJNIObjectPrivate::callStaticObjectMethod("org/qtproject/qt/android/QtNative", + QJniObject objArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/android/QtNative", "listContentsFromTreeUri", "(Landroid/content/Context;Ljava/lang/String;)[Ljava/lang/String;", QtAndroidPrivate::context(), - QJNIObjectPrivate::fromString(path()).object()); + QJniObject::fromString(path()).object()); if (objArray.isValid()) { - QJNIEnvironmentPrivate env; + QJniEnvironment env; const jsize length = env->GetArrayLength(static_cast<jarray>(objArray.object())); for (int i = 0; i != length; ++i) { - m_entries << QJNIObjectPrivate(env->GetObjectArrayElement( + m_entries << QJniObject(env->GetObjectArrayElement( static_cast<jobjectArray>(objArray.object()), i)).toString(); } } diff --git a/src/plugins/platforms/android/androidjniaccessibility.cpp b/src/plugins/platforms/android/androidjniaccessibility.cpp index a4e88d84d4..62460f5e6d 100644 --- a/src/plugins/platforms/android/androidjniaccessibility.cpp +++ b/src/plugins/platforms/android/androidjniaccessibility.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -48,11 +48,9 @@ #include "QtGui/qaccessible.h" #include <QtCore/qmath.h> #include <QtCore/private/qjnihelpers_p.h> -#include <QtCore/private/qjni_p.h> +#include <QtCore/QJniObject> #include <QtGui/private/qhighdpiscaling_p.h> -#include "qdebug.h" - static const char m_qtTag[] = "Qt A11Y"; static const char m_classErrorMsg[] = "Can't find class \"%s\""; @@ -77,8 +75,8 @@ namespace QtAndroidAccessibility void initialize() { - QJNIObjectPrivate::callStaticMethod<void>(QtAndroid::applicationClass(), - "initializeAccessibility"); + QJniObject::callStaticMethod<void>(QtAndroid::applicationClass(), + "initializeAccessibility"); } bool isActive() diff --git a/src/plugins/platforms/android/androidjniclipboard.cpp b/src/plugins/platforms/android/androidjniclipboard.cpp index c20ac456b1..597a56eb1a 100644 --- a/src/plugins/platforms/android/androidjniclipboard.cpp +++ b/src/plugins/platforms/android/androidjniclipboard.cpp @@ -39,7 +39,8 @@ #include "androidjniclipboard.h" #include <QtCore/QUrl> -#include <QtCore/private/qjni_p.h> +#include <QtCore/QJniObject> +#include <QtCore/QJniEnvironment> QT_BEGIN_NAMESPACE @@ -55,9 +56,9 @@ namespace QtAndroidClipboard void setClipboardManager(QAndroidPlatformClipboard *manager) { m_manager = manager; - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "registerClipboardManager"); + QJniObject::callStaticMethod<void>(applicationClass(), "registerClipboardManager"); jclass appClass = QtAndroid::applicationClass(); - QJNIEnvironmentPrivate env; + QJniEnvironment env; if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); return; @@ -65,29 +66,30 @@ namespace QtAndroidClipboard } void clearClipboardData() { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "clearClipData"); + QJniObject::callStaticMethod<void>(applicationClass(), "clearClipData"); } void setClipboardMimeData(QMimeData *data) { clearClipboardData(); if (data->hasText()) { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), - "setClipboardText", "(Ljava/lang/String;)V", - QJNIObjectPrivate::fromString(data->text()).object()); + QJniObject::callStaticMethod<void>(applicationClass(), + "setClipboardText", "(Ljava/lang/String;)V", + QJniObject::fromString(data->text()).object()); } if (data->hasHtml()) { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), - "setClipboardHtml", - "(Ljava/lang/String;Ljava/lang/String;)V", - QJNIObjectPrivate::fromString(data->text()).object(), - QJNIObjectPrivate::fromString(data->html()).object()); + QJniObject::callStaticMethod<void>(applicationClass(), + "setClipboardHtml", + "(Ljava/lang/String;Ljava/lang/String;)V", + QJniObject::fromString(data->text()).object(), + QJniObject::fromString(data->html()).object()); } if (data->hasUrls()) { QList<QUrl> urls = data->urls(); for (const auto &u : qAsConst(urls)) { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "setClipboardUri", - "(Ljava/lang/String;)V", - QJNIObjectPrivate::fromString(u.toEncoded()).object()); + QJniObject::callStaticMethod<void>(applicationClass(), + "setClipboardUri", + "(Ljava/lang/String;)V", + QJniObject::fromString(u.toEncoded()).object()); } } } @@ -95,28 +97,28 @@ namespace QtAndroidClipboard QMimeData *getClipboardMimeData() { QMimeData *data = new QMimeData; - if (QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(), "hasClipboardText")) { - data->setText(QJNIObjectPrivate::callStaticObjectMethod(applicationClass(), - "getClipboardText", - "()Ljava/lang/String;").toString()); + if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardText")) { + data->setText(QJniObject::callStaticObjectMethod(applicationClass(), + "getClipboardText", + "()Ljava/lang/String;").toString()); } - if (QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(), "hasClipboardHtml")) { - data->setHtml(QJNIObjectPrivate::callStaticObjectMethod(applicationClass(), - "getClipboardHtml", - "()Ljava/lang/String;").toString()); + if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardHtml")) { + data->setHtml(QJniObject::callStaticObjectMethod(applicationClass(), + "getClipboardHtml", + "()Ljava/lang/String;").toString()); } - if (QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(), "hasClipboardUri")) { - QJNIObjectPrivate uris = QJNIObjectPrivate::callStaticObjectMethod(applicationClass(), - "getClipboardUris", - "()[Ljava/lang/String;"); + if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardUri")) { + QJniObject uris = QJniObject::callStaticObjectMethod(applicationClass(), + "getClipboardUris", + "()[Ljava/lang/String;"); if (uris.isValid()) { QList<QUrl> urls; - QJNIEnvironmentPrivate env; + QJniEnvironment env; jobjectArray juris = static_cast<jobjectArray>(uris.object()); const jint nUris = env->GetArrayLength(juris); urls.reserve(static_cast<int>(nUris)); for (int i = 0; i < nUris; ++i) - urls << QUrl(QJNIObjectPrivate(env->GetObjectArrayElement(juris, i)).toString()); + urls << QUrl(QJniObject(env->GetObjectArrayElement(juris, i)).toString()); data->setUrls(urls); } } diff --git a/src/plugins/platforms/android/androidjniinput.cpp b/src/plugins/platforms/android/androidjniinput.cpp index 5e4d271fcc..a72682dd23 100644 --- a/src/plugins/platforms/android/androidjniinput.cpp +++ b/src/plugins/platforms/android/androidjniinput.cpp @@ -49,7 +49,6 @@ #include <QPointer> #include <QGuiApplication> -#include <QDebug> #include <QtMath> QT_BEGIN_NAMESPACE @@ -71,27 +70,26 @@ namespace QtAndroidInput #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << ">>> UPDATESELECTION" << selStart << selEnd << candidatesStart << candidatesEnd; #endif - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), - "updateSelection", - "(IIII)V", - selStart, - selEnd, - candidatesStart, - candidatesEnd); + QJniObject::callStaticMethod<void>(applicationClass(), + "updateSelection", + "(IIII)V", + selStart, + selEnd, + candidatesStart, + candidatesEnd); } void showSoftwareKeyboard(int left, int top, int width, int height, int inputHints, int enterKeyType) { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), - "showSoftwareKeyboard", - "(IIIIII)V", - left, - top, - width, - height, - inputHints, - enterKeyType - ); + QJniObject::callStaticMethod<void>(applicationClass(), + "showSoftwareKeyboard", + "(IIIIII)V", + left, + top, + width, + height, + inputHints, + enterKeyType); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug() << "@@@ SHOWSOFTWAREKEYBOARD" << left << top << width << height << inputHints << enterKeyType; #endif @@ -99,7 +97,7 @@ namespace QtAndroidInput void resetSoftwareKeyboard() { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "resetSoftwareKeyboard"); + QJniObject::callStaticMethod<void>(applicationClass(), "resetSoftwareKeyboard"); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ RESETSOFTWAREKEYBOARD"); #endif @@ -107,7 +105,7 @@ namespace QtAndroidInput void hideSoftwareKeyboard() { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "hideSoftwareKeyboard"); + QJniObject::callStaticMethod<void>(applicationClass(), "hideSoftwareKeyboard"); #ifdef QT_DEBUG_ANDROID_IM_PROTOCOL qDebug("@@@ HIDESOFTWAREKEYBOARD"); #endif @@ -125,10 +123,10 @@ namespace QtAndroidInput void updateHandles(int mode, QPoint editMenuPos, uint32_t editButtons, QPoint cursor, QPoint anchor, bool rtl) { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "updateHandles", "(IIIIIIIIZ)V", - mode, editMenuPos.x(), editMenuPos.y(), editButtons, - cursor.x(), cursor.y(), - anchor.x(), anchor.y(), rtl); + QJniObject::callStaticMethod<void>(applicationClass(), "updateHandles", "(IIIIIIIIZ)V", + mode, editMenuPos.x(), editMenuPos.y(), editButtons, + cursor.x(), cursor.y(), + anchor.x(), anchor.y(), rtl); } static void mouseDown(JNIEnv */*env*/, jobject /*thiz*/, jint /*winId*/, jint x, jint y) diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 559cdc6e57..bdd7f492ac 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -40,30 +40,30 @@ #include <dlfcn.h> #include <pthread.h> -#include <semaphore.h> #include <qplugin.h> -#include <qdebug.h> +#include <semaphore.h> -#include "androidjnimain.h" +#include "androidcontentfileengine.h" +#include "androiddeadlockprotector.h" #include "androidjniaccessibility.h" -#include "androidjniinput.h" #include "androidjniclipboard.h" +#include "androidjniinput.h" +#include "androidjnimain.h" #include "androidjnimenu.h" -#include "androidcontentfileengine.h" -#include "androiddeadlockprotector.h" +#include "qandroidassetsfileenginehandler.h" +#include "qandroideventdispatcher.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 <android/asset_manager_jni.h> +#include <android/bitmap.h> +#include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/qjnienvironment.h> +#include <QtCore/qjniobject.h> #include <QtCore/qresource.h> #include <QtCore/qthread.h> -#include <QtCore/private/qjnihelpers_p.h> -#include <QtCore/private/qjni_p.h> #include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qhighdpiscaling_p.h> @@ -205,22 +205,22 @@ namespace QtAndroid void setSystemUiVisibility(SystemUiVisibility uiVisibility) { - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility)); + QJniObject::callStaticMethod<void>(m_applicationClass, "setSystemUiVisibility", "(I)V", jint(uiVisibility)); } void notifyAccessibilityLocationChange() { - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange"); + QJniObject::callStaticMethod<void>(m_applicationClass, "notifyAccessibilityLocationChange"); } void notifyObjectHide(uint accessibilityObjectId) { - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId); + QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectHide","(I)V", accessibilityObjectId); } void notifyObjectFocus(uint accessibilityObjectId) { - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId); + QJniObject::callStaticMethod<void>(m_applicationClass, "notifyObjectFocus","(I)V", accessibilityObjectId); } jobject createBitmap(QImage img, JNIEnv *env) @@ -311,15 +311,15 @@ namespace QtAndroid 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(); + QString manufacturer = QJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER", "Ljava/lang/String;").toString(); + QString model = QJniObject::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; + QJniEnvironment env; if (!env) return -1; @@ -355,26 +355,26 @@ namespace QtAndroid 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)); + QJniObject::callStaticMethod<void>(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<void>(m_applicationClass, - "setViewVisibility", - "(Landroid/view/View;Z)V", - view, - visible); + QJniObject::callStaticMethod<void>(m_applicationClass, + "setViewVisibility", + "(Landroid/view/View;Z)V", + view, + visible); } void setSurfaceGeometry(int surfaceId, const QRect &geometry) @@ -382,7 +382,7 @@ namespace QtAndroid if (surfaceId == -1) return; - QJNIEnvironmentPrivate env; + QJniEnvironment env; if (!env) return; jint x = 0, y = 0, w = -1, h = -1; @@ -393,9 +393,9 @@ namespace QtAndroid h = geometry.height(); } env->CallStaticVoidMethod(m_applicationClass, - m_setSurfaceGeometryMethodID, - surfaceId, - x, y, w, h); + m_setSurfaceGeometryMethodID, + surfaceId, + x, y, w, h); } @@ -411,11 +411,11 @@ namespace QtAndroid m_surfaces.erase(it); } - QJNIEnvironmentPrivate env; + QJniEnvironment env; if (env) env->CallStaticVoidMethod(m_applicationClass, - m_destroySurfaceMethodID, - surfaceId); + m_destroySurfaceMethodID, + surfaceId); } void bringChildToFront(int surfaceId) @@ -423,10 +423,10 @@ namespace QtAndroid if (surfaceId == -1) return; - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, - "bringChildToFront", - "(I)V", - surfaceId); + QJniObject::callStaticMethod<void>(m_applicationClass, + "bringChildToFront", + "(I)V", + surfaceId); } void bringChildToBack(int surfaceId) @@ -434,10 +434,10 @@ namespace QtAndroid if (surfaceId == -1) return; - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, - "bringChildToBack", - "(I)V", - surfaceId); + QJniObject::callStaticMethod<void>(m_applicationClass, + "bringChildToBack", + "(I)V", + surfaceId); } bool blockEventLoopsWhenSuspended() @@ -550,7 +550,7 @@ static jboolean startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) if (m_applicationClass) { qWarning("exit app 0"); - QJNIObjectPrivate::callStaticMethod<void>(m_applicationClass, "quitApp", "()V"); + QJniObject::callStaticMethod<void>(m_applicationClass, "quitApp", "()V"); } sem_post(&m_terminateSemaphore); diff --git a/src/plugins/platforms/android/androidjnimenu.cpp b/src/plugins/platforms/android/androidjnimenu.cpp index fe5de1f882..9b49140335 100644 --- a/src/plugins/platforms/android/androidjnimenu.cpp +++ b/src/plugins/platforms/android/androidjnimenu.cpp @@ -37,10 +37,10 @@ ** ****************************************************************************/ -#include "androidjnimenu.h" #include "androidjnimain.h" -#include "qandroidplatformmenubar.h" +#include "androidjnimenu.h" #include "qandroidplatformmenu.h" +#include "qandroidplatformmenubar.h" #include "qandroidplatformmenuitem.h" #include <QMutex> @@ -50,7 +50,7 @@ #include <QSet> #include <QWindow> #include <QtCore/private/qjnihelpers_p.h> -#include <QtCore/private/qjni_p.h> +#include <QtCore/QJniObject> QT_BEGIN_NAMESPACE @@ -82,12 +82,12 @@ namespace QtAndroidMenu void resetMenuBar() { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "resetOptionsMenu"); + QJniObject::callStaticMethod<void>(applicationClass(), "resetOptionsMenu"); } void openOptionsMenu() { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "openOptionsMenu"); + QJniObject::callStaticMethod<void>(applicationClass(), "openOptionsMenu"); } void showContextMenu(QAndroidPlatformMenu *menu, const QRect &anchorRect, JNIEnv *env) @@ -104,13 +104,14 @@ namespace QtAndroidMenu { QMutexLocker lock(&visibleMenuMutex); if (visibleMenu == menu) { - QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "closeContextMenu"); + QJniObject::callStaticMethod<void>(applicationClass(), "closeContextMenu"); pendingContextMenus.clear(); } else { pendingContextMenus.removeOne(menu); } } + // FIXME void syncMenu(QAndroidPlatformMenu */*menu*/) { // QMutexLocker lock(&visibleMenuMutex); diff --git a/src/plugins/platforms/android/extract.cpp b/src/plugins/platforms/android/extract.cpp index c68dae31e9..bac3c3709e 100644 --- a/src/plugins/platforms/android/extract.cpp +++ b/src/plugins/platforms/android/extract.cpp @@ -39,10 +39,12 @@ -#include <jni.h> +#include <QtCore/QJniEnvironment> + +#include <alloca.h> #include <android/log.h> #include <extract.h> -#include <alloca.h> +#include <jni.h> #include <stdlib.h> #define LOG_TAG "extractSyleInfo" @@ -133,12 +135,15 @@ Java_org_qtproject_qt_android_ExtractStyle_extractChunkInfo20(JNIEnv *env, jobje env->GetByteArrayRegion(chunkObj, 0, chunkSize, reinterpret_cast<jbyte*>(storage)); - if (!env->ExceptionCheck()) - return Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(env, obj, - long(storage)); - else - env->ExceptionClear(); - return 0; + if (QJniEnvironment::exceptionCheckAndClear(env)) + return 0; + + jintArray res = Java_org_qtproject_qt_android_ExtractStyle_extractNativeChunkInfo20(env, obj, + long(storage)); + if (QJniEnvironment::exceptionCheckAndClear(env)) + res = nullptr; + + return res; } static inline void fill9patchOffsets(Res_png_9patch20* patch) { diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp index 6edddf4a66..2dbb1044f0 100644 --- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp +++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp @@ -37,13 +37,15 @@ ** ****************************************************************************/ -#include "qandroidassetsfileenginehandler.h" #include "androidjnimain.h" +#include "qandroidassetsfileenginehandler.h" + #include <optional> #include <QCoreApplication> #include <QList> -#include <QtCore/private/qjni_p.h> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> QT_BEGIN_NAMESPACE @@ -139,16 +141,16 @@ public: FolderIterator(const QString &path) : m_path(path) { - QJNIObjectPrivate files = QJNIObjectPrivate::callStaticObjectMethod(QtAndroid::applicationClass(), + QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(), "listAssetContent", "(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;", - QtAndroid::assets(), QJNIObjectPrivate::fromString(path).object()); + QtAndroid::assets(), QJniObject::fromString(path).object()); if (files.isValid()) { - QJNIEnvironmentPrivate env; + QJniEnvironment env; jobjectArray jFiles = static_cast<jobjectArray>(files.object()); const jint nFiles = env->GetArrayLength(jFiles); for (int i = 0; i < nFiles; ++i) { - AssetItem item{QJNIObjectPrivate::fromLocalRef(env->GetObjectArrayElement(jFiles, i)).toString()}; + AssetItem item{QJniObject::fromLocalRef(env->GetObjectArrayElement(jFiles, i)).toString()}; insert(std::upper_bound(begin(), end(), item, [](const auto &a, const auto &b){ return a.name < b.name; }), item); diff --git a/src/plugins/platforms/android/qandroidinputcontext.cpp b/src/plugins/platforms/android/qandroidinputcontext.cpp index e39f17a26f..06947c9c32 100644 --- a/src/plugins/platforms/android/qandroidinputcontext.cpp +++ b/src/plugins/platforms/android/qandroidinputcontext.cpp @@ -41,26 +41,24 @@ #include <android/log.h> -#include "qandroidinputcontext.h" -#include "androidjnimain.h" +#include "androiddeadlockprotector.h" #include "androidjniinput.h" +#include "androidjnimain.h" #include "qandroideventdispatcher.h" -#include "androiddeadlockprotector.h" +#include "qandroidinputcontext.h" #include "qandroidplatformintegration.h" -#include <QDebug> + +#include <QTextBoundaryFinder> +#include <QTextCharFormat> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> +#include <private/qhighdpiscaling_p.h> #include <qevent.h> #include <qguiapplication.h> +#include <qinputmethod.h> #include <qsharedpointer.h> #include <qthread.h> -#include <qinputmethod.h> #include <qwindow.h> -#include <QtCore/private/qjni_p.h> -#include <private/qhighdpiscaling_p.h> - -#include <QTextCharFormat> -#include <QTextBoundaryFinder> - -#include <QDebug> QT_BEGIN_NAMESPACE @@ -434,7 +432,8 @@ QAndroidInputContext::QAndroidInputContext() , m_batchEditNestingLevel(0) , m_focusObject(0) { - jclass clazz = QJNIEnvironmentPrivate::findClass(QtNativeInputConnectionClassName); + QJniEnvironment env; + jclass clazz = env.findClass(QtNativeInputConnectionClassName); if (Q_UNLIKELY(!clazz)) { qCritical() << "Native registration unable to find class '" << QtNativeInputConnectionClassName @@ -442,7 +441,6 @@ QAndroidInputContext::QAndroidInputContext() return; } - QJNIEnvironmentPrivate env; if (Q_UNLIKELY(env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0)) { qCritical() << "RegisterNatives failed for '" << QtNativeInputConnectionClassName @@ -450,7 +448,7 @@ QAndroidInputContext::QAndroidInputContext() return; } - clazz = QJNIEnvironmentPrivate::findClass(QtExtractedTextClassName); + clazz = env.findClass(QtExtractedTextClassName); if (Q_UNLIKELY(!clazz)) { qCritical() << "Native registration unable to find class '" << QtExtractedTextClassName diff --git a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp index 9ce63dceec..334dd31d9a 100644 --- a/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp +++ b/src/plugins/platforms/android/qandroidplatformdialoghelpers.cpp @@ -90,19 +90,19 @@ bool QAndroidPlatformMessageDialogHelper::show(Qt::WindowFlags windowFlags QString str = htmlText(opt->windowTitle()); if (!str.isEmpty()) - m_javaMessageDialog.callMethod<void>("setTile", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + m_javaMessageDialog.callMethod<void>("setTile", "(Ljava/lang/String;)V", QJniObject::fromString(str).object()); str = htmlText(opt->text()); if (!str.isEmpty()) - m_javaMessageDialog.callMethod<void>("setText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + m_javaMessageDialog.callMethod<void>("setText", "(Ljava/lang/String;)V", QJniObject::fromString(str).object()); str = htmlText(opt->informativeText()); if (!str.isEmpty()) - m_javaMessageDialog.callMethod<void>("setInformativeText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + m_javaMessageDialog.callMethod<void>("setInformativeText", "(Ljava/lang/String;)V", QJniObject::fromString(str).object()); str = htmlText(opt->detailedText()); if (!str.isEmpty()) - m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V", QJNIObjectPrivate::fromString(str).object()); + m_javaMessageDialog.callMethod<void>("setDetailedText", "(Ljava/lang/String;)V", QJniObject::fromString(str).object()); const int * currentLayout = buttonLayout(Qt::Horizontal, AndroidLayout); while (*currentLayout != QPlatformDialogHelper::EOL) { @@ -123,7 +123,7 @@ void QAndroidPlatformMessageDialogHelper::addButtons(QSharedPointer<QMessageDial QString label = b.label; label.remove(QChar('&')); m_javaMessageDialog.callMethod<void>("addButton", "(ILjava/lang/String;)V", b.id, - QJNIObjectPrivate::fromString(label).object()); + QJniObject::fromString(label).object()); } } @@ -131,7 +131,7 @@ void QAndroidPlatformMessageDialogHelper::addButtons(QSharedPointer<QMessageDial StandardButton b = static_cast<StandardButton>(i); if (buttonRole(b) == role && (opt->standardButtons() & i)) { const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(b); - m_javaMessageDialog.callMethod<void>("addButton", "(ILjava/lang/String;)V", i, QJNIObjectPrivate::fromString(text).object()); + m_javaMessageDialog.callMethod<void>("addButton", "(ILjava/lang/String;)V", i, QJniObject::fromString(text).object()); } } } @@ -183,7 +183,8 @@ static JNINativeMethod methods[] = { bool registerNatives(JNIEnv *env) { - jclass clazz = QJNIEnvironmentPrivate::findClass(QtMessageHandlerHelperClassName, env); + QJniEnvironment qenv; + jclass clazz = qenv.findClass(QtMessageHandlerHelperClassName); if (!clazz) { __android_log_print(ANDROID_LOG_FATAL, QtAndroid::qtTagText(), QtAndroid::classErrorMsgFmt() , QtMessageHandlerHelperClassName); diff --git a/src/plugins/platforms/android/qandroidplatformdialoghelpers.h b/src/plugins/platforms/android/qandroidplatformdialoghelpers.h index 694b4c7580..1953b842d5 100644 --- a/src/plugins/platforms/android/qandroidplatformdialoghelpers.h +++ b/src/plugins/platforms/android/qandroidplatformdialoghelpers.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2013 BogDan Vatra <bogdan@kde.org> +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -39,10 +40,13 @@ #ifndef QANDROIDPLATFORMDIALOGHELPERS_H #define QANDROIDPLATFORMDIALOGHELPERS_H + #include <jni.h> -#include <qpa/qplatformdialoghelper.h> + #include <QEventLoop> -#include <private/qjni_p.h> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> +#include <qpa/qplatformdialoghelper.h> QT_BEGIN_NAMESPACE @@ -68,7 +72,7 @@ private: private: int m_buttonId; QEventLoop m_loop; - QJNIObjectPrivate m_javaMessageDialog; + QJniObject m_javaMessageDialog; bool m_shown; }; diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp index acfa4542a1..0b539d1af3 100644 --- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp +++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB) +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -40,10 +41,10 @@ #include "qandroidplatformfiledialoghelper.h" #include <androidjnimain.h> -#include <jni.h> +#include <QtCore/QJniObject> -#include <QMimeType> #include <QMimeDatabase> +#include <QMimeType> #include <QRegularExpression> QT_BEGIN_NAMESPACE @@ -71,9 +72,9 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji return true; } - const QJNIObjectPrivate intent = QJNIObjectPrivate::fromLocalRef(data); + const QJniObject intent = QJniObject::fromLocalRef(data); - const QJNIObjectPrivate uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); + const QJniObject uri = intent.callObjectMethod("getData", "()Landroid/net/Uri;"); if (uri.isValid()) { takePersistableUriPermission(uri); m_selectedFile.append(QUrl(uri.toString())); @@ -83,15 +84,15 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji return true; } - const QJNIObjectPrivate uriClipData = + const QJniObject uriClipData = intent.callObjectMethod("getClipData", "()Landroid/content/ClipData;"); if (uriClipData.isValid()) { const int size = uriClipData.callMethod<jint>("getItemCount"); for (int i = 0; i < size; ++i) { - QJNIObjectPrivate item = uriClipData.callObjectMethod( + QJniObject item = uriClipData.callObjectMethod( "getItemAt", "(I)Landroid/content/ClipData$Item;", i); - QJNIObjectPrivate itemUri = item.callObjectMethod("getUri", "()Landroid/net/Uri;"); + QJniObject itemUri = item.callObjectMethod("getUri", "()Landroid/net/Uri;"); takePersistableUriPermission(itemUri); m_selectedFile.append(itemUri.toString()); } @@ -102,17 +103,17 @@ bool QAndroidPlatformFileDialogHelper::handleActivityResult(jint requestCode, ji return true; } -void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJNIObjectPrivate &uri) +void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJniObject &uri) { - int modeFlags = QJNIObjectPrivate::getStaticField<jint>( + int modeFlags = QJniObject::getStaticField<jint>( JniIntentClass, "FLAG_GRANT_READ_URI_PERMISSION"); if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { - modeFlags |= QJNIObjectPrivate::getStaticField<jint>( + modeFlags |= QJniObject::getStaticField<jint>( JniIntentClass, "FLAG_GRANT_WRITE_URI_PERMISSION"); } - QJNIObjectPrivate contentResolver = m_activity.callObjectMethod( + QJniObject contentResolver = m_activity.callObjectMethod( "getContentResolver", "()Landroid/content/ContentResolver;"); contentResolver.callMethod<void>("takePersistableUriPermission", "(Landroid/net/Uri;I)V", uri.object(), modeFlags); @@ -120,16 +121,16 @@ void QAndroidPlatformFileDialogHelper::takePersistableUriPermission(const QJNIOb void QAndroidPlatformFileDialogHelper::setIntentTitle(const QString &title) { - const QJNIObjectPrivate extraTitle = QJNIObjectPrivate::getStaticObjectField( + const QJniObject extraTitle = QJniObject::getStaticObjectField( JniIntentClass, "EXTRA_TITLE", "Ljava/lang/String;"); m_intent.callObjectMethod("putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", - extraTitle.object(), QJNIObjectPrivate::fromString(title).object()); + extraTitle.object(), QJniObject::fromString(title).object()); } void QAndroidPlatformFileDialogHelper::setOpenableCategory() { - const QJNIObjectPrivate CATEGORY_OPENABLE = QJNIObjectPrivate::getStaticObjectField( + const QJniObject CATEGORY_OPENABLE = QJniObject::getStaticObjectField( JniIntentClass, "CATEGORY_OPENABLE", "Ljava/lang/String;"); m_intent.callObjectMethod("addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", CATEGORY_OPENABLE.object()); @@ -137,7 +138,7 @@ void QAndroidPlatformFileDialogHelper::setOpenableCategory() void QAndroidPlatformFileDialogHelper::setAllowMultipleSelections(bool allowMultiple) { - const QJNIObjectPrivate allowMultipleSelections = QJNIObjectPrivate::getStaticObjectField( + const QJniObject allowMultipleSelections = QJniObject::getStaticObjectField( JniIntentClass, "EXTRA_ALLOW_MULTIPLE", "Ljava/lang/String;"); m_intent.callObjectMethod("putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;", allowMultipleSelections.object(), allowMultiple); @@ -169,17 +170,17 @@ void QAndroidPlatformFileDialogHelper::setMimeTypes() QString type = !mimeTypes.isEmpty() ? mimeTypes.at(0) : QLatin1String("*/*"); m_intent.callObjectMethod("setType", "(Ljava/lang/String;)Landroid/content/Intent;", - QJNIObjectPrivate::fromString(type).object()); + QJniObject::fromString(type).object()); if (!mimeTypes.isEmpty()) { - const QJNIObjectPrivate extraMimeType = QJNIObjectPrivate::getStaticObjectField( + const QJniObject extraMimeType = QJniObject::getStaticObjectField( JniIntentClass, "EXTRA_MIME_TYPES", "Ljava/lang/String;"); - QJNIObjectPrivate mimeTypesArray = QJNIObjectPrivate::callStaticObjectMethod( + QJniObject mimeTypesArray = QJniObject::callStaticObjectMethod( "org/qtproject/qt/android/QtNative", "getStringArray", "(Ljava/lang/String;)[Ljava/lang/String;", - QJNIObjectPrivate::fromString(mimeTypes.join(",")).object()); + QJniObject::fromString(mimeTypes.join(",")).object()); m_intent.callObjectMethod( "putExtra", "(Ljava/lang/String;[Ljava/lang/String;)Landroid/content/Intent;", @@ -187,11 +188,11 @@ void QAndroidPlatformFileDialogHelper::setMimeTypes() } } -QJNIObjectPrivate QAndroidPlatformFileDialogHelper::getFileDialogIntent(const QString &intentType) +QJniObject QAndroidPlatformFileDialogHelper::getFileDialogIntent(const QString &intentType) { - const QJNIObjectPrivate ACTION_OPEN_DOCUMENT = QJNIObjectPrivate::getStaticObjectField( + const QJniObject ACTION_OPEN_DOCUMENT = QJniObject::getStaticObjectField( JniIntentClass, intentType.toLatin1(), "Ljava/lang/String;"); - return QJNIObjectPrivate(JniIntentClass, "(Ljava/lang/String;)V", + return QJniObject(JniIntentClass, "(Ljava/lang/String;)V", ACTION_OPEN_DOCUMENT.object()); } diff --git a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h index a7f9aaa358..2d3cb49a96 100644 --- a/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h +++ b/src/plugins/platforms/android/qandroidplatformfiledialoghelper.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2019 Klaralvdalens Datakonsult AB (KDAB) +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -41,11 +42,11 @@ #define QANDROIDPLATFORMFILEDIALOGHELPER_H #include <jni.h> + #include <QEventLoop> -#include <qpa/qplatformdialoghelper.h> +#include <QtCore/QJniObject> #include <QtCore/private/qjnihelpers_p.h> -#include <private/qjni_p.h> -#include <QEventLoop> +#include <qpa/qplatformdialoghelper.h> QT_BEGIN_NAMESPACE @@ -73,8 +74,8 @@ public: bool handleActivityResult(jint requestCode, jint resultCode, jobject data) override; private: - QJNIObjectPrivate getFileDialogIntent(const QString &intentType); - void takePersistableUriPermission(const QJNIObjectPrivate &uri); + QJniObject getFileDialogIntent(const QString &intentType); + void takePersistableUriPermission(const QJniObject &uri); void setIntentTitle(const QString &title); void setOpenableCategory(); void setAllowMultipleSelections(bool allowMultiple); @@ -82,8 +83,8 @@ private: QEventLoop m_eventLoop; QList<QUrl> m_selectedFile; - QJNIObjectPrivate m_intent; - const QJNIObjectPrivate m_activity; + QJniObject m_intent; + const QJniObject m_activity; }; } diff --git a/src/plugins/platforms/android/qandroidplatformforeignwindow.h b/src/plugins/platforms/android/qandroidplatformforeignwindow.h index af1eee5499..8e45395838 100644 --- a/src/plugins/platforms/android/qandroidplatformforeignwindow.h +++ b/src/plugins/platforms/android/qandroidplatformforeignwindow.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -42,7 +42,8 @@ #include "androidsurfaceclient.h" #include "qandroidplatformwindow.h" -#include <QtCore/private/qjni_p.h> + +#include <QtCore/QJniObject> QT_BEGIN_NAMESPACE @@ -61,7 +62,7 @@ public: private: int m_surfaceId; - QJNIObjectPrivate m_view; + QJniObject m_view; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 8048bd6cff..1468343831 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -39,35 +40,34 @@ #include "qandroidplatformintegration.h" -#include <QtCore/private/qjni_p.h> -#include <QtGui/private/qguiapplication_p.h> -#include <QGuiApplication> -#include <QOpenGLContext> -#include <QOffscreenSurface> -#include <QtGui/private/qoffscreensurface_p.h> -#include <QThread> - -#include <QtGui/private/qeglpbuffer_p.h> -#include <qpa/qwindowsysteminterface.h> -#include <qpa/qplatformwindow.h> -#include <qpa/qplatformoffscreensurface.h> - -#include "androidjnimain.h" #include "androidjniaccessibility.h" +#include "androidjnimain.h" #include "qabstracteventdispatcher.h" #include "qandroideventdispatcher.h" -#include "qandroidplatformbackingstore.h" #include "qandroidplatformaccessibility.h" +#include "qandroidplatformbackingstore.h" #include "qandroidplatformclipboard.h" -#include "qandroidplatformforeignwindow.h" #include "qandroidplatformfontdatabase.h" +#include "qandroidplatformforeignwindow.h" +#include "qandroidplatformoffscreensurface.h" #include "qandroidplatformopenglcontext.h" #include "qandroidplatformopenglwindow.h" #include "qandroidplatformscreen.h" #include "qandroidplatformservices.h" #include "qandroidplatformtheme.h" #include "qandroidsystemlocale.h" -#include "qandroidplatformoffscreensurface.h" + +#include <QGuiApplication> +#include <QOffscreenSurface> +#include <QOpenGLContext> +#include <QThread> +#include <QtCore/QJniObject> +#include <QtGui/private/qeglpbuffer_p.h> +#include <QtGui/private/qguiapplication_p.h> +#include <QtGui/private/qoffscreensurface_p.h> +#include <qpa/qplatformoffscreensurface.h> +#include <qpa/qplatformwindow.h> +#include <qpa/qwindowsysteminterface.h> #include <jni.h> @@ -200,17 +200,17 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ m_accessibility = new QAndroidPlatformAccessibility(); #endif // QT_NO_ACCESSIBILITY - QJNIObjectPrivate javaActivity(QtAndroid::activity()); + QJniObject javaActivity(QtAndroid::activity()); if (!javaActivity.isValid()) javaActivity = QtAndroid::service(); if (javaActivity.isValid()) { - QJNIObjectPrivate resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); - QJNIObjectPrivate configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); + QJniObject resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); + QJniObject configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); int touchScreen = configuration.getField<jint>("touchscreen"); - if (touchScreen == QJNIObjectPrivate::getStaticField<jint>("android/content/res/Configuration", "TOUCHSCREEN_FINGER") - || touchScreen == QJNIObjectPrivate::getStaticField<jint>("android/content/res/Configuration", "TOUCHSCREEN_STYLUS")) + if (touchScreen == QJniObject::getStaticField<jint>("android/content/res/Configuration", "TOUCHSCREEN_FINGER") + || touchScreen == QJniObject::getStaticField<jint>("android/content/res/Configuration", "TOUCHSCREEN_STYLUS")) { m_touchDevice = new QPointingDevice; m_touchDevice->setType(QInputDevice::DeviceType::TouchScreen); @@ -219,16 +219,16 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ | QPointingDevice::Capability::Pressure | QPointingDevice::Capability::NormalizedPosition); - QJNIObjectPrivate pm = javaActivity.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); + QJniObject pm = javaActivity.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); Q_ASSERT(pm.isValid()); if (pm.callMethod<jboolean>("hasSystemFeature","(Ljava/lang/String;)Z", - QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND", "Ljava/lang/String;").object())) { + QJniObject::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH_JAZZHAND", "Ljava/lang/String;").object())) { m_touchDevice->setMaximumTouchPoints(10); } else if (pm.callMethod<jboolean>("hasSystemFeature","(Ljava/lang/String;)Z", - QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT", "Ljava/lang/String;").object())) { + QJniObject::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT", "Ljava/lang/String;").object())) { m_touchDevice->setMaximumTouchPoints(4); } else if (pm.callMethod<jboolean>("hasSystemFeature","(Ljava/lang/String;)Z", - QJNIObjectPrivate::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH", "Ljava/lang/String;").object())) { + QJniObject::getStaticObjectField("android/content/pm/PackageManager", "FEATURE_TOUCHSCREEN_MULTITOUCH", "Ljava/lang/String;").object())) { m_touchDevice->setMaximumTouchPoints(2); } QWindowSystemInterface::registerInputDevice(m_touchDevice); @@ -236,11 +236,14 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList ¶ auto contentResolver = javaActivity.callObjectMethod("getContentResolver", "()Landroid/content/ContentResolver;"); Q_ASSERT(contentResolver.isValid()); - QJNIObjectPrivate txtShowPassValue = QJNIObjectPrivate::callStaticObjectMethod("android/provider/Settings$System", - "getString", - "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;", - contentResolver.object(), - QJNIObjectPrivate::getStaticObjectField("android/provider/Settings$System", "TEXT_SHOW_PASSWORD", "Ljava/lang/String;").object()); + QJniObject txtShowPassValue = QJniObject::callStaticObjectMethod( + "android/provider/Settings$System", + "getString", + "(Landroid/content/ContentResolver;Ljava/lang/String;)Ljava/lang/String;", + contentResolver.object(), + QJniObject::getStaticObjectField("android/provider/Settings$System", + "TEXT_SHOW_PASSWORD", + "Ljava/lang/String;").object()); if (txtShowPassValue.isValid()) { bool ok = false; const int txtShowPass = txtShowPassValue.toString().toInt(&ok); diff --git a/src/plugins/platforms/android/qandroidplatformmenu.cpp b/src/plugins/platforms/android/qandroidplatformmenu.cpp index 3431997a53..1ac836faf2 100644 --- a/src/plugins/platforms/android/qandroidplatformmenu.cpp +++ b/src/plugins/platforms/android/qandroidplatformmenu.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> +** Copyright (C) 2021 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -37,10 +38,12 @@ ** ****************************************************************************/ +#include "androidjnimenu.h" #include "qandroidplatformmenu.h" #include "qandroidplatformmenuitem.h" -#include "androidjnimenu.h" -#include <QtCore/private/qjni_p.h> + +#include <QtCore/qjnienvironment.h> +#include <QtCore/qjniobject.h> QT_BEGIN_NAMESPACE @@ -152,7 +155,7 @@ void QAndroidPlatformMenu::showPopup(const QWindow *parentWindow, const QRect &t Q_UNUSED(parentWindow); Q_UNUSED(item); setVisible(true); - QtAndroidMenu::showContextMenu(this, targetRect, QJNIEnvironmentPrivate()); + QtAndroidMenu::showContextMenu(this, targetRect, QJniEnvironment()); } QPlatformMenuItem *QAndroidPlatformMenu::menuItemForTag(quintptr tag) const diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp index 333fc2a25f..f69e9e389f 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp @@ -40,20 +40,20 @@ #include "qandroidplatformopenglwindow.h" -#include "qandroidplatformscreen.h" +#include "androiddeadlockprotector.h" #include "androidjnimain.h" #include "qandroideventdispatcher.h" -#include "androiddeadlockprotector.h" +#include "qandroidplatformscreen.h" #include <QSurfaceFormat> #include <QtGui/private/qwindow_p.h> #include <QtGui/qguiapplication.h> -#include <qpa/qwindowsysteminterface.h> -#include <qpa/qplatformscreen.h> #include <QtGui/private/qeglconvenience_p.h> #include <android/native_window.h> #include <android/native_window_jni.h> +#include <qpa/qplatformscreen.h> +#include <qpa/qwindowsysteminterface.h> QT_BEGIN_NAMESPACE @@ -175,9 +175,9 @@ void QAndroidPlatformOpenGLWindow::applicationStateChanged(Qt::ApplicationState void QAndroidPlatformOpenGLWindow::createEgl(EGLConfig config) { clearEgl(); - QJNIEnvironmentPrivate env; + QJniEnvironment env; m_nativeWindow = ANativeWindow_fromSurface(env, m_androidSurfaceObject.object()); - m_androidSurfaceObject = QJNIObjectPrivate(); + m_androidSurfaceObject = QJniObject(); m_eglSurface = eglCreateWindowSurface(m_eglDisplay, config, m_nativeWindow, NULL); m_format = q_glFormatFromConfig(m_eglDisplay, config, window()->requestedFormat()); if (Q_UNLIKELY(m_eglSurface == EGL_NO_SURFACE)) { diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.h b/src/plugins/platforms/android/qandroidplatformopenglwindow.h index d3072f766d..b8f1a5f9fc 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglwindow.h +++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.h @@ -41,13 +41,15 @@ #ifndef QANDROIDPLATFORMOPENGLWINDOW_H #define QANDROIDPLATFORMOPENGLWINDOW_H -#include <EGL/egl.h> -#include <QWaitCondition> -#include <QtCore/private/qjni_p.h> - #include "androidsurfaceclient.h" #include "qandroidplatformwindow.h" +#include <QWaitCondition> +#include <QtCore/qjnienvironment.h> +#include <QtCore/qjniobject.h> + +#include <EGL/egl.h> + QT_BEGIN_NAMESPACE class QAndroidPlatformOpenGLWindow : public QAndroidPlatformWindow, public AndroidSurfaceClient @@ -77,7 +79,7 @@ private: EGLNativeWindowType m_nativeWindow = nullptr; int m_nativeSurfaceId = -1; - QJNIObjectPrivate m_androidSurfaceObject; + QJniObject m_androidSurfaceObject; QWaitCondition m_surfaceWaitCondition; QSurfaceFormat m_format; QRect m_oldGeometry; diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h index 5dc158e351..4cdb0351bd 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.h +++ b/src/plugins/platforms/android/qandroidplatformscreen.h @@ -41,14 +41,14 @@ #ifndef QANDROIDPLATFORMSCREEN_H #define QANDROIDPLATFORMSCREEN_H -#include <qpa/qplatformscreen.h> +#include "androidsurfaceclient.h" + #include <QList> #include <QPainter> #include <QTimer> #include <QWaitCondition> -#include <QtCore/private/qjni_p.h> - -#include "androidsurfaceclient.h" +#include <QtCore/QJniObject> +#include <qpa/qplatformscreen.h> #include <android/native_window.h> diff --git a/src/plugins/platforms/android/qandroidplatformservices.cpp b/src/plugins/platforms/android/qandroidplatformservices.cpp index c095613ce7..b5f829d3d3 100644 --- a/src/plugins/platforms/android/qandroidplatformservices.cpp +++ b/src/plugins/platforms/android/qandroidplatformservices.cpp @@ -38,11 +38,12 @@ ****************************************************************************/ #include "qandroidplatformservices.h" -#include <QUrl> -#include <QFile> + #include <QDebug> +#include <QFile> #include <QMimeDatabase> -#include <QtCore/private/qjni_p.h> +#include <QUrl> +#include <QtCore/QJniObject> #include <private/qjnihelpers_p.h> QT_BEGIN_NAMESPACE @@ -66,9 +67,9 @@ bool QAndroidPlatformServices::openUrl(const QUrl &theUrl) mime = mimeDb.mimeTypeForUrl(url).name(); } - QJNIObjectPrivate urlString = QJNIObjectPrivate::fromString(url.toString()); - QJNIObjectPrivate mimeString = QJNIObjectPrivate::fromString(mime); - return QJNIObjectPrivate::callStaticMethod<jboolean>( + QJniObject urlString = QJniObject::fromString(url.toString()); + QJniObject mimeString = QJniObject::fromString(mime); + return QJniObject::callStaticMethod<jboolean>( QtAndroid::applicationClass(), "openURL", "(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)Z", QtAndroidPrivate::context(), urlString.object(), mimeString.object()); diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp index 6a1a51b272..8f2a37626a 100644 --- a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp @@ -37,15 +37,15 @@ ** ****************************************************************************/ -#include "qandroidplatformvulkanwindow.h" -#include "qandroidplatformscreen.h" +#include "androiddeadlockprotector.h" #include "androidjnimain.h" #include "qandroideventdispatcher.h" -#include "androiddeadlockprotector.h" +#include "qandroidplatformscreen.h" +#include "qandroidplatformvulkanwindow.h" #include <QSurfaceFormat> -#include <qpa/qwindowsysteminterface.h> #include <qpa/qplatformscreen.h> +#include <qpa/qwindowsysteminterface.h> #include <android/native_window.h> #include <android/native_window_jni.h> @@ -172,7 +172,7 @@ VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface() if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid()) return &m_vkSurface; - QJNIEnvironmentPrivate env; + QJniEnvironment env; m_nativeWindow = ANativeWindow_fromSurface(env, m_androidSurfaceObject.object()); VkAndroidSurfaceCreateInfoKHR surfaceInfo; diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.h b/src/plugins/platforms/android/qandroidplatformvulkanwindow.h index 19eca98720..bc11f468d6 100644 --- a/src/plugins/platforms/android/qandroidplatformvulkanwindow.h +++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.h @@ -46,13 +46,13 @@ #define VK_USE_PLATFORM_ANDROID_KHR -#include <QWaitCondition> -#include <QtCore/private/qjni_p.h> - #include "androidsurfaceclient.h" +#include "qandroidplatformvulkaninstance.h" #include "qandroidplatformwindow.h" -#include "qandroidplatformvulkaninstance.h" +#include <QWaitCondition> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> QT_BEGIN_NAMESPACE @@ -77,7 +77,7 @@ private: int m_nativeSurfaceId; ANativeWindow *m_nativeWindow; - QJNIObjectPrivate m_androidSurfaceObject; + QJniObject m_androidSurfaceObject; QWaitCondition m_surfaceWaitCondition; QSurfaceFormat m_format; QRect m_oldGeometry; diff --git a/src/plugins/platforms/android/qandroidsystemlocale.cpp b/src/plugins/platforms/android/qandroidsystemlocale.cpp index bb6aa0dda1..45387b53c7 100644 --- a/src/plugins/platforms/android/qandroidsystemlocale.cpp +++ b/src/plugins/platforms/android/qandroidsystemlocale.cpp @@ -37,14 +37,15 @@ ** ****************************************************************************/ -#include "qandroidsystemlocale.h" #include "androidjnimain.h" -#include <QtCore/private/qjni_p.h> -#include <QtCore/private/qjnihelpers_p.h> +#include "qandroidsystemlocale.h" #include "qdatetime.h" #include "qstringlist.h" #include "qvariant.h" +#include <QtCore/private/qjnihelpers_p.h> +#include <QtCore/QJniObject> + QT_BEGIN_NAMESPACE QAndroidSystemLocale::QAndroidSystemLocale() : m_locale(QLocale::C) @@ -55,17 +56,17 @@ void QAndroidSystemLocale::getLocaleFromJava() const { QWriteLocker locker(&m_lock); - QJNIObjectPrivate javaLocaleObject; - QJNIObjectPrivate javaActivity(QtAndroid::activity()); + QJniObject javaLocaleObject; + QJniObject javaActivity(QtAndroid::activity()); if (!javaActivity.isValid()) javaActivity = QtAndroid::service(); if (javaActivity.isValid()) { - QJNIObjectPrivate resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); - QJNIObjectPrivate configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); + QJniObject resources = javaActivity.callObjectMethod("getResources", "()Landroid/content/res/Resources;"); + QJniObject configuration = resources.callObjectMethod("getConfiguration", "()Landroid/content/res/Configuration;"); javaLocaleObject = configuration.getObjectField("locale", "Ljava/util/Locale;"); } else { - javaLocaleObject = QJNIObjectPrivate::callStaticObjectMethod("java/util/Locale", "getDefault", "()Ljava/util/Locale;"); + javaLocaleObject = QJniObject::callStaticObjectMethod("java/util/Locale", "getDefault", "()Ljava/util/Locale;"); } QString languageCode = javaLocaleObject.callObjectMethod("getLanguage", "()Ljava/lang/String;").toString(); @@ -165,9 +166,9 @@ QVariant QAndroidSystemLocale::query(QueryType type, QVariant in) const Q_ASSERT_X(false, Q_FUNC_INFO, "This can't happen."); case UILanguages: { if (QtAndroidPrivate::androidSdkVersion() >= 24) { - QJNIObjectPrivate localeListObject = - QJNIObjectPrivate::callStaticObjectMethod("android/os/LocaleList", "getDefault", - "()Landroid/os/LocaleList;"); + QJniObject localeListObject = + QJniObject::callStaticObjectMethod("android/os/LocaleList", "getDefault", + "()Landroid/os/LocaleList;"); if (localeListObject.isValid()) { QString lang = localeListObject.callObjectMethod("toLanguageTags", "()Ljava/lang/String;").toString(); diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt index bb7a665c76..3326070727 100644 --- a/tests/auto/corelib/kernel/CMakeLists.txt +++ b/tests/auto/corelib/kernel/CMakeLists.txt @@ -45,3 +45,7 @@ endif() if(QT_FEATURE_private_tests) add_subdirectory(qproperty) endif() +if(ANDROID AND NOT ANDROID_EMBEDDED) + add_subdirectory(qjnienvironment) + add_subdirectory(qjniobject) +endif() diff --git a/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt b/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt new file mode 100644 index 0000000000..b6c88ede33 --- /dev/null +++ b/tests/auto/corelib/kernel/qjnienvironment/CMakeLists.txt @@ -0,0 +1,8 @@ +##################################################################### +## tst_qjnienvironment Test: +##################################################################### + +qt_internal_add_test(tst_qjnienvironment + SOURCES + tst_qjnienvironment.cpp +) diff --git a/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp new file mode 100644 index 0000000000..08cf0e4831 --- /dev/null +++ b/tests/auto/corelib/kernel/qjnienvironment/tst_qjnienvironment.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QJniEnvironment> +#include <QtTest/QtTest> + +class tst_QJniEnvironment : public QObject +{ + Q_OBJECT + +private slots: + void jniEnv(); + void javaVM(); +}; + +void tst_QJniEnvironment::jniEnv() +{ + QJniEnvironment env; + JavaVM *javaVM = env.javaVM(); + QVERIFY(javaVM); + + { + // JNI environment should now be attached to the current thread + JNIEnv *jni = 0; + QCOMPARE(javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_OK); + + JNIEnv *e = env; + QVERIFY(e); + + QCOMPARE(env->GetVersion(), JNI_VERSION_1_6); + + // try to find an existing class + QVERIFY(env->FindClass("java/lang/Object")); + QVERIFY(!env->ExceptionCheck()); + + // try to find a nonexistent class + QVERIFY(!env->FindClass("this/doesnt/Exist")); + QVERIFY(env->ExceptionCheck()); + env->ExceptionClear(); + + QVERIFY(env->FindClass("java/lang/Object")); + QVERIFY(!QJniEnvironment::exceptionCheckAndClear(env)); + + // try to find a nonexistent class + QVERIFY(!env->FindClass("this/doesnt/Exist")); + QVERIFY(QJniEnvironment::exceptionCheckAndClear(env)); + + // try to find an existing class with QJniEnvironment + QJniEnvironment env; + QVERIFY(env.findClass("java/lang/Object")); + + // try to find a nonexistent class + QVERIFY(!env.findClass("this/doesnt/Exist")); + + // clear exception with member function + QVERIFY(!env->FindClass("this/doesnt/Exist")); + QVERIFY(env.exceptionCheckAndClear()); + } + + // The env does not detach automatically, even if it goes out of scope. The only way it can + // be detached is if it's done explicitly, or if the thread we attached to gets killed (TLS clean-up). + JNIEnv *jni = nullptr; + QCOMPARE(javaVM->GetEnv((void**)&jni, JNI_VERSION_1_6), JNI_OK); +} + +void tst_QJniEnvironment::javaVM() +{ + QJniEnvironment env; + JavaVM *javaVM = env.javaVM(); + QVERIFY(javaVM); + + QCOMPARE(env.javaVM(), javaVM); + + JavaVM *vm = 0; + QCOMPARE(env->GetJavaVM(&vm), JNI_OK); + QCOMPARE(env.javaVM(), vm); +} + +QTEST_MAIN(tst_QJniEnvironment) + +#include "tst_qjnienvironment.moc" diff --git a/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt new file mode 100644 index 0000000000..b0f5d0b640 --- /dev/null +++ b/tests/auto/corelib/kernel/qjniobject/CMakeLists.txt @@ -0,0 +1,16 @@ +##################################################################### +## tst_qjniobject Test: +##################################################################### + +qt_internal_add_test(tst_qjniobject + SOURCES + tst_qjniobject.cpp +) + +if(ANDROID) + set_property(TARGET tst_qjniobject APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR + ${CMAKE_CURRENT_SOURCE_DIR}/testdata + ) + # QTBUG-88840 # special case + qt_android_generate_deployment_settings(tst_qjniobject) # special case +endif() diff --git a/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java new file mode 100644 index 0000000000..4d6db749a5 --- /dev/null +++ b/tests/auto/corelib/kernel/qjniobject/testdata/src/org/qtproject/qt/android/testdata/QtJniObjectTestClass.java @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt.android.testdatapackage; + +public class QtJniObjectTestClass +{ + static final byte A_BYTE_VALUE = 127; + static final short A_SHORT_VALUE = 32767; + static final int A_INT_VALUE = 060701; + static final long A_LONG_VALUE = 060701; + static final float A_FLOAT_VALUE = 1.0f; + static final double A_DOUBLE_VALUE = 1.0; + static final boolean A_BOOLEAN_VALUE = true; + static final char A_CHAR_VALUE = 'Q'; + static final String A_STRING_OBJECT = "TEST_DATA_STRING"; + static final Class A_CLASS_OBJECT = QtJniObjectTestClass.class; + static final Object A_OBJECT_OBJECT = new QtJniObjectTestClass(); + static final Throwable A_THROWABLE_OBJECT = new Throwable(A_STRING_OBJECT); + + // -------------------------------------------------------------------------------------------- + public static void staticVoidMethod() { return; } + public static void staticVoidMethodWithArgs(int a, boolean b, char c) { return; } + + public void voidMethod() { return; } + public void voidMethodWithArgs(int a, boolean b, char c) { return; } + + // -------------------------------------------------------------------------------------------- + public static boolean staticBooleanMethod() { return A_BOOLEAN_VALUE; } + public static boolean staticBooleanMethodWithArgs(boolean a, boolean b, boolean c) + { return staticBooleanMethod(); } + + public boolean booleanMethod() { return staticBooleanMethod(); } + public boolean booleanMethodWithArgs(boolean a, boolean b, boolean c) + { return staticBooleanMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static byte staticByteMethod() { return A_BYTE_VALUE; } + public static byte staticByteMethodWithArgs(byte a, byte b, byte c) { return staticByteMethod(); } + + public byte byteMethod() { return staticByteMethod(); } + public byte byteMethodWithArgs(byte a, byte b, byte c) + { return staticByteMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static char staticCharMethod() { return A_CHAR_VALUE; } + public static char staticCharMethodWithArgs(char a, char b, char c) { return staticCharMethod(); } + + public char charMethod() { return staticCharMethod(); } + public char charMethodWithArgs(char a, char b, char c) + { return staticCharMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static short staticShortMethod() { return A_SHORT_VALUE; } + public static short staticShortMethodWithArgs(short a, short b, short c) { return staticShortMethod(); } + + public short shortMethod() { return staticShortMethod(); } + public short shortMethodWithArgs(short a, short b, short c) + { return staticShortMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static int staticIntMethod() { return A_INT_VALUE; } + public static int staticIntMethodWithArgs(int a, int b, int c) { return staticIntMethod(); } + + public int intMethod() { return staticIntMethod(); } + public int intMethodWithArgs(int a, int b, int c) { return staticIntMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static long staticLongMethod() { return A_LONG_VALUE; } + public static long staticLongMethodWithArgs(long a, long b, long c) { return staticLongMethod(); } + + public long longMethod() { return staticLongMethod(); } + public long longMethodWithArgs(long a, long b, long c) + { return staticLongMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static float staticFloatMethod() { return A_FLOAT_VALUE; } + public static float staticFloatMethodWithArgs(float a, float b, float c) { return staticFloatMethod(); } + + public float floatMethod() { return staticFloatMethod(); } + public float floatMethodWithArgs(float a, float b, float c) + { return staticFloatMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static double staticDoubleMethod() { return A_DOUBLE_VALUE; } + public static double staticDoubleMethodWithArgs(double a, double b, double c) + { return staticDoubleMethod(); } + + public double doubleMethod() { return staticDoubleMethod(); } + public double doubleMethodWithArgs(double a, double b, double c) + { return staticDoubleMethodWithArgs(a, b, c); } + + // -------------------------------------------------------------------------------------------- + public static Object staticObjectMethod() { return A_OBJECT_OBJECT; } + public Object objectMethod() { return staticObjectMethod(); } + + // -------------------------------------------------------------------------------------------- + public static Class staticClassMethod() { return A_CLASS_OBJECT; } + public Class classMethod() { return staticClassMethod(); } + + // -------------------------------------------------------------------------------------------- + public static String staticStringMethod() { return A_STRING_OBJECT; } + public String stringMethod() { return staticStringMethod(); } + + // -------------------------------------------------------------------------------------------- + public static Throwable staticThrowableMethod() { return A_THROWABLE_OBJECT; } + public Throwable throwableMethod() { return staticThrowableMethod(); } + + // -------------------------------------------------------------------------------------------- + public static Object[] staticObjectArrayMethod() + { Object[] array = { new Object(), new Object(), new Object() }; return array; } + public Object[] objectArrayMethod() { return staticObjectArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static boolean[] staticBooleanArrayMethod() + { boolean[] array = { true, true, true }; return array; } + public boolean[] booleanArrayMethod() { return staticBooleanArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static byte[] staticByteArrayMethod() + { byte[] array = { 'a', 'b', 'c' }; return array; } + public byte[] byteArrayMethod() { return staticByteArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static char[] staticCharArrayMethod() + { char[] array = { 'a', 'b', 'c' }; return array; } + public char[] charArrayMethod() { return staticCharArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static short[] staticShortArrayMethod() { short[] array = { 3, 2, 1 }; return array; } + public short[] shortArrayMethod() { return staticShortArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static int[] staticIntArrayMethod() { int[] array = { 3, 2, 1 }; return array; } + public int[] intArrayMethod() { return staticIntArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static long[] staticLongArrayMethod() + { long[] array = { 3, 2, 1 }; return array; } + public long[] longArrayMethod() { return staticLongArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static float[] staticFloatArrayMethod() + { float[] array = { 1.0f, 2.0f, 3.0f }; return array; } + public float[] floatArrayMethod() { return staticFloatArrayMethod(); } + + // -------------------------------------------------------------------------------------------- + public static double[] staticDoubleArrayMethod() + { double[] array = { 3.0, 2.0, 1.0 }; return array; } + public double[] doubleArrayMethod() { return staticDoubleArrayMethod(); } +} + diff --git a/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp new file mode 100644 index 0000000000..96637a72a6 --- /dev/null +++ b/tests/auto/corelib/kernel/qjniobject/tst_qjniobject.cpp @@ -0,0 +1,1054 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QString> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> +#include <QtTest> + +static const char testClassName[] = "org/qtproject/qt/android/testdatapackage/QtJniObjectTestClass"; + +static const jbyte A_BYTE_VALUE = 127; +static const jshort A_SHORT_VALUE = 32767; +static const jint A_INT_VALUE = 060701; +static const jlong A_LONG_VALUE = 060701; +static const jfloat A_FLOAT_VALUE = 1.0; +static const jdouble A_DOUBLE_VALUE = 1.0; +static const jchar A_CHAR_VALUE = 'Q'; + +static QString A_STRING_OBJECT() +{ + return QStringLiteral("TEST_DATA_STRING"); +} + +class tst_QJniObject : public QObject +{ + Q_OBJECT + +public: + tst_QJniObject(); + +private slots: + void initTestCase(); + + void ctor(); + void callMethodTest(); + void callObjectMethodTest(); + void stringConvertionTest(); + void compareOperatorTests(); + void callStaticObjectMethodClassName(); + void callStaticObjectMethod(); + void callStaticBooleanMethodClassName(); + void callStaticBooleanMethod(); + void callStaticCharMethodClassName(); + void callStaticCharMethod(); + void callStaticIntMethodClassName(); + void callStaticIntMethod(); + void callStaticByteMethodClassName(); + void callStaticByteMethod(); + void callStaticDoubleMethodClassName(); + void callStaticDoubleMethod(); + void callStaticFloatMethodClassName(); + void callStaticFloatMethod(); + void callStaticLongMethodClassName(); + void callStaticLongMethod(); + void callStaticShortMethodClassName(); + void callStaticShortMethod(); + void getStaticObjectFieldClassName(); + void getStaticObjectField(); + void getStaticIntFieldClassName(); + void getStaticIntField(); + void getStaticByteFieldClassName(); + void getStaticByteField(); + void getStaticLongFieldClassName(); + void getStaticLongField(); + void getStaticDoubleFieldClassName(); + void getStaticDoubleField(); + void getStaticFloatFieldClassName(); + void getStaticFloatField(); + void getStaticShortFieldClassName(); + void getStaticShortField(); + void getStaticCharFieldClassName(); + void getStaticCharField(); + void getBooleanField(); + void getIntField(); + void templateApiCheck(); + void isClassAvailable(); + void fromLocalRef(); + + void cleanupTestCase(); +}; + +tst_QJniObject::tst_QJniObject() +{ +} + +void tst_QJniObject::initTestCase() +{ +} + +void tst_QJniObject::cleanupTestCase() +{ +} + +void tst_QJniObject::ctor() +{ + { + QJniObject object; + QVERIFY(!object.isValid()); + } + + { + QJniObject object("java/lang/String"); + QVERIFY(object.isValid()); + } + + { + QJniObject string = QJniObject::fromString(QLatin1String("Hello, Java")); + QJniObject object("java/lang/String", "(Ljava/lang/String;)V", string.object<jstring>()); + QVERIFY(object.isValid()); + QCOMPARE(string.toString(), object.toString()); + } + + { + QJniEnvironment env; + jclass javaStringClass = env->FindClass("java/lang/String"); + QJniObject string(javaStringClass); + QVERIFY(string.isValid()); + } + + { + QJniEnvironment env; + const QString qString = QLatin1String("Hello, Java"); + jclass javaStringClass = env->FindClass("java/lang/String"); + QJniObject string = QJniObject::fromString(qString); + QJniObject stringCpy(javaStringClass, "(Ljava/lang/String;)V", string.object<jstring>()); + QVERIFY(stringCpy.isValid()); + QCOMPARE(qString, stringCpy.toString()); + } +} + +void tst_QJniObject::callMethodTest() +{ + { + QJniObject jString1 = QJniObject::fromString(QLatin1String("Hello, Java")); + QJniObject jString2 = QJniObject::fromString(QLatin1String("hELLO, jAVA")); + QVERIFY(jString1 != jString2); + + const jboolean isEmpty = jString1.callMethod<jboolean>("isEmpty"); + QVERIFY(!isEmpty); + + const jint ret = jString1.callMethod<jint>("compareToIgnoreCase", + "(Ljava/lang/String;)I", + jString2.object<jstring>()); + QVERIFY(0 == ret); + } + + { + jlong jLong = 100; + QJniObject longObject("java/lang/Long", "(J)V", jLong); + jlong ret = longObject.callMethod<jlong>("longValue"); + QCOMPARE(ret, jLong); + } +} + +void tst_QJniObject::callObjectMethodTest() +{ + const QString qString = QLatin1String("Hello, Java"); + QJniObject jString = QJniObject::fromString(qString); + const QString qStringRet = jString.callObjectMethod<jstring>("toUpperCase").toString(); + QCOMPARE(qString.toUpper(), qStringRet); + + QJniObject subString = jString.callObjectMethod("substring", + "(II)Ljava/lang/String;", + 0, 4); + QCOMPARE(subString.toString(), qString.mid(0, 4)); +} + +void tst_QJniObject::stringConvertionTest() +{ + const QString qString(QLatin1String("Hello, Java")); + QJniObject jString = QJniObject::fromString(qString); + QVERIFY(jString.isValid()); + QString qStringRet = jString.toString(); + QCOMPARE(qString, qStringRet); +} + +void tst_QJniObject::compareOperatorTests() +{ + QString str("hello!"); + QJniObject stringObject = QJniObject::fromString(str); + + jobject obj = stringObject.object(); + jobject jobj = stringObject.object<jobject>(); + jstring jsobj = stringObject.object<jstring>(); + + QVERIFY(obj == stringObject); + QVERIFY(jobj == stringObject); + QVERIFY(stringObject == jobj); + QVERIFY(jsobj == stringObject); + QVERIFY(stringObject == jsobj); + + QJniObject stringObject3 = stringObject.object<jstring>(); + QVERIFY(stringObject3 == stringObject); + + QJniObject stringObject2 = QJniObject::fromString(str); + QVERIFY(stringObject != stringObject2); + + jstring jstrobj = 0; + QJniObject invalidStringObject; + QVERIFY(invalidStringObject == jstrobj); + + QVERIFY(jstrobj != stringObject); + QVERIFY(stringObject != jstrobj); + QVERIFY(!invalidStringObject.isValid()); +} + +void tst_QJniObject::callStaticObjectMethodClassName() +{ + QJniObject formatString = QJniObject::fromString(QLatin1String("test format")); + QVERIFY(formatString.isValid()); + + QVERIFY(QJniObject::isClassAvailable("java/lang/String")); + QJniObject returnValue = QJniObject::callStaticObjectMethod("java/lang/String", + "format", + "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + + QString returnedString = returnValue.toString(); + + QCOMPARE(returnedString, QString::fromLatin1("test format")); +} + +void tst_QJniObject::callStaticObjectMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/String"); + QVERIFY(cls != 0); + + QJniObject formatString = QJniObject::fromString(QLatin1String("test format")); + QVERIFY(formatString.isValid()); + + QJniObject returnValue = QJniObject::callStaticObjectMethod(cls, + "format", + "(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;", + formatString.object<jstring>(), + jobjectArray(0)); + QVERIFY(returnValue.isValid()); + + QString returnedString = returnValue.toString(); + + QCOMPARE(returnedString, QString::fromLatin1("test format")); +} + +void tst_QJniObject::callStaticBooleanMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Boolean"); + QVERIFY(cls != 0); + + { + QJniObject parameter = QJniObject::fromString("true"); + QVERIFY(parameter.isValid()); + + jboolean b = QJniObject::callStaticMethod<jboolean>(cls, + "parseBoolean", + "(Ljava/lang/String;)Z", + parameter.object<jstring>()); + QVERIFY(b); + } + + { + QJniObject parameter = QJniObject::fromString("false"); + QVERIFY(parameter.isValid()); + + jboolean b = QJniObject::callStaticMethod<jboolean>(cls, + "parseBoolean", + "(Ljava/lang/String;)Z", + parameter.object<jstring>()); + QVERIFY(!b); + } +} + +void tst_QJniObject::callStaticBooleanMethodClassName() +{ + { + QJniObject parameter = QJniObject::fromString("true"); + QVERIFY(parameter.isValid()); + + jboolean b = QJniObject::callStaticMethod<jboolean>("java/lang/Boolean", + "parseBoolean", + "(Ljava/lang/String;)Z", + parameter.object<jstring>()); + QVERIFY(b); + } + + { + QJniObject parameter = QJniObject::fromString("false"); + QVERIFY(parameter.isValid()); + + jboolean b = QJniObject::callStaticMethod<jboolean>("java/lang/Boolean", + "parseBoolean", + "(Ljava/lang/String;)Z", + parameter.object<jstring>()); + QVERIFY(!b); + } +} + +void tst_QJniObject::callStaticByteMethodClassName() +{ + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jbyte returnValue = QJniObject::callStaticMethod<jbyte>("java/lang/Byte", + "parseByte", + "(Ljava/lang/String;)B", + parameter.object<jstring>()); + QCOMPARE(returnValue, jbyte(number.toInt())); +} + +void tst_QJniObject::callStaticByteMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Byte"); + QVERIFY(cls != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jbyte returnValue = QJniObject::callStaticMethod<jbyte>(cls, + "parseByte", + "(Ljava/lang/String;)B", + parameter.object<jstring>()); + QCOMPARE(returnValue, jbyte(number.toInt())); +} + +void tst_QJniObject::callStaticIntMethodClassName() +{ + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jint returnValue = QJniObject::callStaticMethod<jint>("java/lang/Integer", + "parseInt", + "(Ljava/lang/String;)I", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toInt()); +} + + +void tst_QJniObject::callStaticIntMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Integer"); + QVERIFY(cls != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jint returnValue = QJniObject::callStaticMethod<jint>(cls, + "parseInt", + "(Ljava/lang/String;)I", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toInt()); +} + +void tst_QJniObject::callStaticCharMethodClassName() +{ + jchar returnValue = QJniObject::callStaticMethod<jchar>("java/lang/Character", + "toUpperCase", + "(C)C", + jchar('a')); + QCOMPARE(returnValue, jchar('A')); +} + + +void tst_QJniObject::callStaticCharMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Character"); + QVERIFY(cls != 0); + + jchar returnValue = QJniObject::callStaticMethod<jchar>(cls, + "toUpperCase", + "(C)C", + jchar('a')); + QCOMPARE(returnValue, jchar('A')); +} + +void tst_QJniObject::callStaticDoubleMethodClassName () +{ + QString number = QString::number(123.45); + QJniObject parameter = QJniObject::fromString(number); + + jdouble returnValue = QJniObject::callStaticMethod<jdouble>("java/lang/Double", + "parseDouble", + "(Ljava/lang/String;)D", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toDouble()); +} + + +void tst_QJniObject::callStaticDoubleMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Double"); + QVERIFY(cls != 0); + + QString number = QString::number(123.45); + QJniObject parameter = QJniObject::fromString(number); + + jdouble returnValue = QJniObject::callStaticMethod<jdouble>(cls, + "parseDouble", + "(Ljava/lang/String;)D", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toDouble()); +} + +void tst_QJniObject::callStaticFloatMethodClassName() +{ + QString number = QString::number(123.45); + QJniObject parameter = QJniObject::fromString(number); + + jfloat returnValue = QJniObject::callStaticMethod<jfloat>("java/lang/Float", + "parseFloat", + "(Ljava/lang/String;)F", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toFloat()); +} + + +void tst_QJniObject::callStaticFloatMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Float"); + QVERIFY(cls != 0); + + QString number = QString::number(123.45); + QJniObject parameter = QJniObject::fromString(number); + + jfloat returnValue = QJniObject::callStaticMethod<jfloat>(cls, + "parseFloat", + "(Ljava/lang/String;)F", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toFloat()); +} + +void tst_QJniObject::callStaticShortMethodClassName() +{ + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jshort returnValue = QJniObject::callStaticMethod<jshort>("java/lang/Short", + "parseShort", + "(Ljava/lang/String;)S", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toShort()); +} + + +void tst_QJniObject::callStaticShortMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Short"); + QVERIFY(cls != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jshort returnValue = QJniObject::callStaticMethod<jshort>(cls, + "parseShort", + "(Ljava/lang/String;)S", + parameter.object<jstring>()); + QCOMPARE(returnValue, number.toShort()); +} + +void tst_QJniObject::callStaticLongMethodClassName() +{ + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jlong returnValue = QJniObject::callStaticMethod<jlong>("java/lang/Long", + "parseLong", + "(Ljava/lang/String;)J", + parameter.object<jstring>()); + QCOMPARE(returnValue, jlong(number.toLong())); +} + +void tst_QJniObject::callStaticLongMethod() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Long"); + QVERIFY(cls != 0); + + QString number = QString::number(123); + QJniObject parameter = QJniObject::fromString(number); + + jlong returnValue = QJniObject::callStaticMethod<jlong>(cls, + "parseLong", + "(Ljava/lang/String;)J", + parameter.object<jstring>()); + QCOMPARE(returnValue, jlong(number.toLong())); +} + +void tst_QJniObject::getStaticObjectFieldClassName() +{ + { + QJniObject boolObject = QJniObject::getStaticObjectField<jobject>("java/lang/Boolean", + "FALSE", + "Ljava/lang/Boolean;"); + QVERIFY(boolObject.isValid()); + + jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); + QVERIFY(!booleanValue); + } + + { + QJniObject boolObject = QJniObject::getStaticObjectField<jobject>("java/lang/Boolean", + "TRUE", + "Ljava/lang/Boolean;"); + QVERIFY(boolObject.isValid()); + + jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); + QVERIFY(booleanValue); + } + + { + QJniObject boolObject = QJniObject::getStaticObjectField("java/lang/Boolean", + "FALSE", + "Ljava/lang/Boolean;"); + QVERIFY(boolObject.isValid()); + jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); + QVERIFY(!booleanValue); + } +} + +void tst_QJniObject::getStaticObjectField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Boolean"); + QVERIFY(cls != 0); + + { + QJniObject boolObject = QJniObject::getStaticObjectField<jobject>(cls, + "FALSE", + "Ljava/lang/Boolean;"); + QVERIFY(boolObject.isValid()); + + jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); + QVERIFY(!booleanValue); + } + + { + QJniObject boolObject = QJniObject::getStaticObjectField<jobject>(cls, + "TRUE", + "Ljava/lang/Boolean;"); + QVERIFY(boolObject.isValid()); + + jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); + QVERIFY(booleanValue); + } + + { + QJniObject boolObject = QJniObject::getStaticObjectField(cls, + "FALSE", + "Ljava/lang/Boolean;"); + QVERIFY(boolObject.isValid()); + + jboolean booleanValue = boolObject.callMethod<jboolean>("booleanValue"); + QVERIFY(!booleanValue); + } +} + +void tst_QJniObject::getStaticIntFieldClassName() +{ + jint i = QJniObject::getStaticField<jint>("java/lang/Double", "SIZE"); + QCOMPARE(i, 64); +} + +void tst_QJniObject::getStaticIntField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Double"); + QVERIFY(cls != 0); + + jint i = QJniObject::getStaticField<jint>(cls, "SIZE"); + QCOMPARE(i, 64); +} + +void tst_QJniObject::getStaticByteFieldClassName() +{ + jbyte i = QJniObject::getStaticField<jbyte>("java/lang/Byte", "MAX_VALUE"); + QCOMPARE(i, jbyte(127)); +} + +void tst_QJniObject::getStaticByteField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Byte"); + QVERIFY(cls != 0); + + jbyte i = QJniObject::getStaticField<jbyte>(cls, "MAX_VALUE"); + QCOMPARE(i, jbyte(127)); +} + +void tst_QJniObject::getStaticLongFieldClassName() +{ + jlong i = QJniObject::getStaticField<jlong>("java/lang/Long", "MAX_VALUE"); + QCOMPARE(i, jlong(9223372036854775807L)); +} + +void tst_QJniObject::getStaticLongField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Long"); + QVERIFY(cls != 0); + + jlong i = QJniObject::getStaticField<jlong>(cls, "MAX_VALUE"); + QCOMPARE(i, jlong(9223372036854775807L)); +} + +void tst_QJniObject::getStaticDoubleFieldClassName() +{ + jdouble i = QJniObject::getStaticField<jdouble>("java/lang/Double", "NaN"); + jlong *k = reinterpret_cast<jlong*>(&i); + QCOMPARE(*k, jlong(0x7ff8000000000000L)); +} + +void tst_QJniObject::getStaticDoubleField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Double"); + QVERIFY(cls != 0); + + jdouble i = QJniObject::getStaticField<jdouble>(cls, "NaN"); + jlong *k = reinterpret_cast<jlong*>(&i); + QCOMPARE(*k, jlong(0x7ff8000000000000L)); +} + +void tst_QJniObject::getStaticFloatFieldClassName() +{ + jfloat i = QJniObject::getStaticField<jfloat>("java/lang/Float", "NaN"); + unsigned *k = reinterpret_cast<unsigned*>(&i); + QCOMPARE(*k, unsigned(0x7fc00000)); +} + +void tst_QJniObject::getStaticFloatField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Float"); + QVERIFY(cls != 0); + + jfloat i = QJniObject::getStaticField<jfloat>(cls, "NaN"); + unsigned *k = reinterpret_cast<unsigned*>(&i); + QCOMPARE(*k, unsigned(0x7fc00000)); +} + +void tst_QJniObject::getStaticShortFieldClassName() +{ + jshort i = QJniObject::getStaticField<jshort>("java/lang/Short", "MAX_VALUE"); + QCOMPARE(i, jshort(32767)); +} + +void tst_QJniObject::getStaticShortField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Short"); + QVERIFY(cls != 0); + + jshort i = QJniObject::getStaticField<jshort>(cls, "MAX_VALUE"); + QCOMPARE(i, jshort(32767)); +} + +void tst_QJniObject::getStaticCharFieldClassName() +{ + jchar i = QJniObject::getStaticField<jchar>("java/lang/Character", "MAX_VALUE"); + QCOMPARE(i, jchar(0xffff)); +} + +void tst_QJniObject::getStaticCharField() +{ + QJniEnvironment env; + jclass cls = env->FindClass("java/lang/Character"); + QVERIFY(cls != 0); + + jchar i = QJniObject::getStaticField<jchar>(cls, "MAX_VALUE"); + QCOMPARE(i, jchar(0xffff)); +} + + +void tst_QJniObject::getBooleanField() +{ + QJniObject obj("org/qtproject/qt/android/QtActivityDelegate"); + + QVERIFY(obj.isValid()); + QVERIFY(!obj.getField<jboolean>("m_fullScreen")); +} + +void tst_QJniObject::getIntField() +{ + QJniObject obj("org/qtproject/qt/android/QtActivityDelegate"); + + QVERIFY(obj.isValid()); + jint res = obj.getField<jint>("m_currentRotation"); + QCOMPARE(res, -1); +} + +void tst_QJniObject::templateApiCheck() +{ + QJniObject testClass(testClassName); + QVERIFY(testClass.isValid()); + + // void --------------------------------------------------------------------------------------- + QJniObject::callStaticMethod<void>(testClassName, "staticVoidMethod"); + QJniObject::callStaticMethod<void>(testClassName, + "staticVoidMethodWithArgs", + "(IZC)V", + 1, + true, + 'c'); + + testClass.callMethod<void>("voidMethod"); + testClass.callMethod<void>("voidMethodWithArgs", "(IZC)V", 1, true, 'c'); + + // jboolean ----------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jboolean>(testClassName, "staticBooleanMethod")); + QVERIFY(QJniObject::callStaticMethod<jboolean>(testClassName, + "staticBooleanMethodWithArgs", + "(ZZZ)Z", + true, + true, + true)); + + QVERIFY(testClass.callMethod<jboolean>("booleanMethod")); + QVERIFY(testClass.callMethod<jboolean>("booleanMethodWithArgs", + "(ZZZ)Z", + true, + true, + true)); + + // jbyte -------------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName, + "staticByteMethod") == A_BYTE_VALUE); + QVERIFY(QJniObject::callStaticMethod<jbyte>(testClassName, + "staticByteMethodWithArgs", + "(BBB)B", + 1, + 1, + 1) == A_BYTE_VALUE); + + QVERIFY(testClass.callMethod<jbyte>("byteMethod") == A_BYTE_VALUE); + QVERIFY(testClass.callMethod<jbyte>("byteMethodWithArgs", "(BBB)B", 1, 1, 1) == A_BYTE_VALUE); + + // jchar -------------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jchar>(testClassName, + "staticCharMethod") == A_CHAR_VALUE); + QVERIFY(QJniObject::callStaticMethod<jchar>(testClassName, + "staticCharMethodWithArgs", + "(CCC)C", + jchar(1), + jchar(1), + jchar(1)) == A_CHAR_VALUE); + + QVERIFY(testClass.callMethod<jchar>("charMethod") == A_CHAR_VALUE); + QVERIFY(testClass.callMethod<jchar>("charMethodWithArgs", + "(CCC)C", + jchar(1), + jchar(1), + jchar(1)) == A_CHAR_VALUE); + + // jshort ------------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jshort>(testClassName, + "staticShortMethod") == A_SHORT_VALUE); + QVERIFY(QJniObject::callStaticMethod<jshort>(testClassName, + "staticShortMethodWithArgs", + "(SSS)S", + jshort(1), + jshort(1), + jshort(1)) == A_SHORT_VALUE); + + QVERIFY(testClass.callMethod<jshort>("shortMethod") == A_SHORT_VALUE); + QVERIFY(testClass.callMethod<jshort>("shortMethodWithArgs", + "(SSS)S", + jshort(1), + jshort(1), + jshort(1)) == A_SHORT_VALUE); + + // jint --------------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jint>(testClassName, + "staticIntMethod") == A_INT_VALUE); + QVERIFY(QJniObject::callStaticMethod<jint>(testClassName, + "staticIntMethodWithArgs", + "(III)I", + jint(1), + jint(1), + jint(1)) == A_INT_VALUE); + + QVERIFY(testClass.callMethod<jint>("intMethod") == A_INT_VALUE); + QVERIFY(testClass.callMethod<jint>("intMethodWithArgs", + "(III)I", + jint(1), + jint(1), + jint(1)) == A_INT_VALUE); + + // jlong -------------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jlong>(testClassName, + "staticLongMethod") == A_LONG_VALUE); + QVERIFY(QJniObject::callStaticMethod<jlong>(testClassName, + "staticLongMethodWithArgs", + "(JJJ)J", + jlong(1), + jlong(1), + jlong(1)) == A_LONG_VALUE); + + QVERIFY(testClass.callMethod<jlong>("longMethod") == A_LONG_VALUE); + QVERIFY(testClass.callMethod<jlong>("longMethodWithArgs", + "(JJJ)J", + jlong(1), + jlong(1), + jlong(1)) == A_LONG_VALUE); + + // jfloat ------------------------------------------------------------------------------------- + QVERIFY(QJniObject::callStaticMethod<jfloat>(testClassName, + "staticFloatMethod") == A_FLOAT_VALUE); + QVERIFY(QJniObject::callStaticMethod<jfloat>(testClassName, + "staticFloatMethodWithArgs", + "(FFF)F", + jfloat(1.1), + jfloat(1.1), + jfloat(1.1)) == A_FLOAT_VALUE); + + QVERIFY(testClass.callMethod<jfloat>("floatMethod") == A_FLOAT_VALUE); + QVERIFY(testClass.callMethod<jfloat>("floatMethodWithArgs", + "(FFF)F", + jfloat(1.1), + jfloat(1.1), + jfloat(1.1)) == A_FLOAT_VALUE); + + // jdouble ------------------------------------------------------------------------------------ + QVERIFY(QJniObject::callStaticMethod<jdouble>(testClassName, + "staticDoubleMethod") == A_DOUBLE_VALUE); + QVERIFY(QJniObject::callStaticMethod<jdouble>(testClassName, + "staticDoubleMethodWithArgs", + "(DDD)D", + jdouble(1.1), + jdouble(1.1), + jdouble(1.1)) == A_DOUBLE_VALUE); + + QVERIFY(testClass.callMethod<jdouble>("doubleMethod") == A_DOUBLE_VALUE); + QVERIFY(testClass.callMethod<jdouble>("doubleMethodWithArgs", + "(DDD)D", + jdouble(1.1), + jdouble(1.1), + jdouble(1.1)) == A_DOUBLE_VALUE); + + // jobject ------------------------------------------------------------------------------------ + { + QJniObject res = QJniObject::callStaticObjectMethod<jobject>(testClassName, + "staticObjectMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jobject>("objectMethod"); + QVERIFY(res.isValid()); + } + + // jclass ------------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jclass>(testClassName, + "staticClassMethod"); + QVERIFY(res.isValid()); + QJniEnvironment env; + QVERIFY(env->IsInstanceOf(testClass.object(), res.object<jclass>())); + } + + { + QJniObject res = testClass.callObjectMethod<jclass>("classMethod"); + QVERIFY(res.isValid()); + QJniEnvironment env; + QVERIFY(env->IsInstanceOf(testClass.object(), res.object<jclass>())); + } + // jstring ------------------------------------------------------------------------------------ + { + QJniObject res = QJniObject::callStaticObjectMethod<jstring>(testClassName, + "staticStringMethod"); + QVERIFY(res.isValid()); + QVERIFY(res.toString() == A_STRING_OBJECT()); + } + + { + QJniObject res = testClass.callObjectMethod<jstring>("stringMethod"); + QVERIFY(res.isValid()); + QVERIFY(res.toString() == A_STRING_OBJECT()); + + } + // jthrowable --------------------------------------------------------------------------------- + { + // The Throwable object the same message (see: "getMessage()") as A_STRING_OBJECT + QJniObject res = QJniObject::callStaticObjectMethod<jthrowable>(testClassName, + "staticThrowableMethod"); + QVERIFY(res.isValid()); + QVERIFY(res.callObjectMethod<jstring>("getMessage").toString() == A_STRING_OBJECT()); + } + + { + QJniObject res = testClass.callObjectMethod<jthrowable>("throwableMethod"); + QVERIFY(res.isValid()); + QVERIFY(res.callObjectMethod<jstring>("getMessage").toString() == A_STRING_OBJECT()); + } + + // jobjectArray ------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jobjectArray>(testClassName, + "staticObjectArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jobjectArray>("objectArrayMethod"); + QVERIFY(res.isValid()); + } + + // jbooleanArray ------------------------------------------------------------------------------ + { + QJniObject res = QJniObject::callStaticObjectMethod<jbooleanArray>(testClassName, + "staticBooleanArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jbooleanArray>("booleanArrayMethod"); + QVERIFY(res.isValid()); + } + + // jbyteArray --------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jbyteArray>(testClassName, + "staticByteArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jbyteArray>("byteArrayMethod"); + QVERIFY(res.isValid()); + } + + // jcharArray --------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jcharArray>(testClassName, + "staticCharArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jcharArray>("charArrayMethod"); + QVERIFY(res.isValid()); + } + + // jshortArray -------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jshortArray>(testClassName, + "staticShortArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jshortArray>("shortArrayMethod"); + QVERIFY(res.isValid()); + } + + // jintArray ---------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jintArray>(testClassName, + "staticIntArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jintArray>("intArrayMethod"); + QVERIFY(res.isValid()); + } + + // jlongArray --------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jlongArray>(testClassName, + "staticLongArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jlongArray>("longArrayMethod"); + QVERIFY(res.isValid()); + } + + // jfloatArray -------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jfloatArray>(testClassName, + "staticFloatArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jfloatArray>("floatArrayMethod"); + QVERIFY(res.isValid()); + } + + // jdoubleArray ------------------------------------------------------------------------------- + { + QJniObject res = QJniObject::callStaticObjectMethod<jdoubleArray>(testClassName, + "staticDoubleArrayMethod"); + QVERIFY(res.isValid()); + } + + { + QJniObject res = testClass.callObjectMethod<jdoubleArray>("doubleArrayMethod"); + QVERIFY(res.isValid()); + } + +} + +void tst_QJniObject::isClassAvailable() +{ + QVERIFY(QJniObject::isClassAvailable("java/lang/String")); + QVERIFY(!QJniObject::isClassAvailable("class/not/Available")); + QVERIFY(QJniObject::isClassAvailable("org/qtproject/qt/android/QtActivityDelegate")); +} + +void tst_QJniObject::fromLocalRef() +{ + const int limit = 512 + 1; + QJniEnvironment env; + for (int i = 0; i != limit; ++i) + QJniObject o = QJniObject::fromLocalRef(env->FindClass("java/lang/String")); +} + +QTEST_MAIN(tst_QJniObject) + +#include "tst_qjniobject.moc" |