Android: Implement backend register class

This class will be used by java delegates to register their supported
functionalities to the Qt/C++ side.

It registers two native functions: register- and
unregisterBackend, which will be used by the aforementioned
delegates.

It will be used by C++ classes which currently use
QtAndroid::qtActivityDelegate() to access a JNI object which implements
all these features in one big class.

Task-number: QTBUG-118874
Change-Id: I23a7e433104c20b96c08b682a96cfaec98ecb4a9
Reviewed-by: Tinja Paavoseppä <tinja.paavoseppa@qt.io>
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
bb10
Petri Virkkunen 2024-03-04 15:01:21 +02:00
parent 353dd10a1c
commit 124eaa6b22
7 changed files with 143 additions and 2 deletions

View File

@ -40,6 +40,7 @@ set(java_sources
src/org/qtproject/qt/android/QtView.java
src/org/qtproject/qt/android/QtEmbeddedViewInterface.java
src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java
src/org/qtproject/qt/android/BackendRegister.java
)
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android

View File

@ -0,0 +1,9 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
package org.qtproject.qt.android;
class BackendRegister
{
static native void registerBackend(Class interfaceType, Object interfaceObject);
static native void unregisterBackend(Class interfaceType);
}

View File

@ -41,6 +41,7 @@ qt_internal_add_plugin(QAndroidIntegrationPlugin
qandroidplatformwindow.cpp qandroidplatformwindow.h
qandroidsystemlocale.cpp qandroidsystemlocale.h
androidwindowembedding.cpp androidwindowembedding.h
androidbackendregister.cpp androidbackendregister.h
NO_UNITY_BUILD_SOURCES
# Conflicting symbols and macros with androidjnimain.cpp
# TODO: Unify the usage of FIND_AND_CHECK_CLASS, and similar

View File

@ -0,0 +1,52 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "androidbackendregister.h"
#include "androidjnimain.h"
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcAndroidBackendRegister, "qt.qpa.androidbackendregister")
Q_DECLARE_JNI_CLASS(BackendRegister, "org/qtproject/qt/android/BackendRegister");
bool AndroidBackendRegister::registerNatives()
{
return QtJniTypes::BackendRegister::registerNativeMethods(
{ Q_JNI_NATIVE_SCOPED_METHOD(registerBackend, AndroidBackendRegister),
Q_JNI_NATIVE_SCOPED_METHOD(unregisterBackend, AndroidBackendRegister) });
}
void AndroidBackendRegister::registerBackend(JNIEnv *, jclass, jclass interfaceClass,
jobject interface)
{
if (AndroidBackendRegister *reg = QtAndroid::backendRegister()) {
const QJniObject classObject(static_cast<jobject>(interfaceClass));
QString name = classObject.callMethod<jstring>("getName").toString();
name.replace('.', '/');
QMutexLocker lock(&reg->m_registerMutex);
reg->m_register[name] = QJniObject(interface);
} else {
qCWarning(lcAndroidBackendRegister)
<< "AndroidBackendRegister pointer is null, cannot register functionality";
}
}
void AndroidBackendRegister::unregisterBackend(JNIEnv *, jclass, jclass interfaceClass)
{
if (AndroidBackendRegister *reg = QtAndroid::backendRegister()) {
const QJniObject classObject(static_cast<jobject>(interfaceClass));
QString name = classObject.callMethod<jstring>("getName").toString();
name.replace('.', '/');
QMutexLocker lock(&reg->m_registerMutex);
reg->m_register.remove(name);
} else {
qCWarning(lcAndroidBackendRegister)
<< "AndroidBackendRegister pointer is null, cannot unregister functionality";
}
}
QT_END_NAMESPACE

View File

@ -0,0 +1,67 @@
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef ANDROIDBACKENDREGISTER_H
#define ANDROIDBACKENDREGISTER_H
#include <type_traits>
#include <QtCore/qjnienvironment.h>
#include <QtCore/qjnitypes.h>
#include <QtCore/qjniobject.h>
#include <QtCore/qstring.h>
#include <QtCore/qmap.h>
#include <QtCore/qmutex.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcAndroidBackendRegister)
template <typename T>
using ValidInterfaceType = std::enable_if_t<std::is_base_of_v<QtJniTypes::JObjectBase, T>, bool>;
class AndroidBackendRegister
{
public:
static bool registerNatives();
template <typename T, ValidInterfaceType<T> = true>
[[nodiscard]] T getInterface()
{
QMutexLocker lock(&m_registerMutex);
return m_register.value(QString(QtJniTypes::Traits<T>::className().data()));
}
template <typename Object>
using IsObjectType =
typename std::disjunction<std::is_base_of<QJniObject, Object>,
std::is_base_of<QtJniTypes::JObjectBase, Object>>;
template <typename Interface, typename Ret, typename... Args,
ValidInterfaceType<Interface> = true>
auto callInterface(const char *func, Args... args)
{
if (const auto obj = getInterface<Interface>(); obj.isValid())
return obj.template callMethod<Ret, Args...>(func, std::forward<Args>(args)...);
if constexpr (IsObjectType<Ret>::value)
return Ret(QJniObject());
if constexpr (!std::is_same_v<Ret, void>)
return Ret{};
}
private:
QMutex m_registerMutex;
QMap<QString, QJniObject> m_register;
static void registerBackend(JNIEnv *, jclass, jclass interfaceClass, jobject interface);
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(registerBackend)
static void unregisterBackend(JNIEnv *, jclass, jclass interfaceClass);
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(unregisterBackend)
};
QT_END_NAMESPACE
#endif // ANDROIDBACKENDREGISTER_H

View File

@ -85,7 +85,7 @@ static double m_density = 1.0;
static AndroidAssetsFileEngineHandler *m_androidAssetsFileEngineHandler = nullptr;
static AndroidContentFileEngineHandler *m_androidContentFileEngineHandler = nullptr;
static AndroidBackendRegister *m_backendRegister = nullptr;
static const char m_qtTag[] = "Qt";
static const char m_classErrorMsg[] = "Can't find class \"%s\"";
@ -387,6 +387,11 @@ namespace QtAndroid
return m_assets;
}
AndroidBackendRegister *backendRegister()
{
return m_backendRegister;
}
} // namespace QtAndroid
static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString)
@ -397,6 +402,7 @@ static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring pa
m_androidAssetsFileEngineHandler = new AndroidAssetsFileEngineHandler();
m_androidContentFileEngineHandler = new AndroidContentFileEngineHandler();
m_mainLibraryHnd = nullptr;
m_backendRegister = new AndroidBackendRegister();
const QStringList argsList = QProcess::splitCommand(QJniObject(paramsString).toString());
@ -544,6 +550,8 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
m_androidPlatformIntegration = nullptr;
delete m_androidAssetsFileEngineHandler;
m_androidAssetsFileEngineHandler = nullptr;
delete m_backendRegister;
m_backendRegister = nullptr;
sem_post(&m_exitSemaphore);
}
@ -891,7 +899,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
|| !QtAndroidDialogHelpers::registerNatives(env)
|| !QAndroidPlatformClipboard::registerNatives(env)
|| !QAndroidPlatformWindow::registerNatives(env)
|| !QtAndroidWindowEmbedding::registerNatives(env)) {
|| !QtAndroidWindowEmbedding::registerNatives(env)
|| !AndroidBackendRegister::registerNatives()) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1;
}

View File

@ -13,6 +13,7 @@
#include <QImage>
#include <private/qjnihelpers_p.h>
#include <QtCore/QJniObject>
#include <androidbackendregister.h>
QT_BEGIN_NAMESPACE
@ -33,6 +34,7 @@ namespace QtAndroid
{
QBasicMutex *platformInterfaceMutex();
QAndroidPlatformIntegration *androidPlatformIntegration();
AndroidBackendRegister *backendRegister();
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration);
void setQtThread(QThread *thread);
void setViewVisibility(jobject view, bool visible);