From 457566c96f420c452c4709e1e1942933c1ea2c5d Mon Sep 17 00:00:00 2001 From: Assam Boudjelthia Date: Mon, 18 Sep 2023 12:02:47 +0300 Subject: Android: Move clipboard management to own class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all clipboard management logic outside of QtNative and to own QtClipboardManager class. Also, don't keep any keep Activity objects under it to avoid memory leaks, the native c++ clipboard manager should be responsible of passing a context when needed instead. As a pass-by, use newer JNI APIs for C++ QtAndroidClipboard code. Task-number: QTBUG-118077 Change-Id: I61726e84a75918d80329f753e9e1c6ebde179bf4 Reviewed-by: Tinja Paavoseppä --- src/plugins/platforms/android/CMakeLists.txt | 3 +- .../platforms/android/androidjniclipboard.cpp | 96 ---------------------- .../platforms/android/androidjniclipboard.h | 27 ------ src/plugins/platforms/android/androidjnimain.cpp | 5 +- .../android/qandroidplatformclipboard.cpp | 84 +++++++++++++++++-- .../platforms/android/qandroidplatformclipboard.h | 16 ++++ 6 files changed, 99 insertions(+), 132 deletions(-) delete mode 100644 src/plugins/platforms/android/androidjniclipboard.cpp delete mode 100644 src/plugins/platforms/android/androidjniclipboard.h (limited to 'src/plugins/platforms') diff --git a/src/plugins/platforms/android/CMakeLists.txt b/src/plugins/platforms/android/CMakeLists.txt index 0b24e52d9a..40def7ba77 100644 --- a/src/plugins/platforms/android/CMakeLists.txt +++ b/src/plugins/platforms/android/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause ##################################################################### @@ -14,7 +14,6 @@ qt_internal_add_plugin(QAndroidIntegrationPlugin androidcontentfileengine.cpp androidcontentfileengine.h androiddeadlockprotector.h androidjniaccessibility.cpp androidjniaccessibility.h - androidjniclipboard.cpp androidjniclipboard.h androidjniinput.cpp androidjniinput.h androidjnimain.cpp androidjnimain.h androidjnimenu.cpp androidjnimenu.h diff --git a/src/plugins/platforms/android/androidjniclipboard.cpp b/src/plugins/platforms/android/androidjniclipboard.cpp deleted file mode 100644 index d510f43049..0000000000 --- a/src/plugins/platforms/android/androidjniclipboard.cpp +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2012 BogDan Vatra -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include "androidjniclipboard.h" -#include -#include -#include - -QT_BEGIN_NAMESPACE - -using namespace QtAndroid; -namespace QtAndroidClipboard -{ - QAndroidPlatformClipboard *m_manager = nullptr; - - static JNINativeMethod methods[] = { - {"onClipboardDataChanged", "()V", (void *)onClipboardDataChanged} - }; - - void setClipboardManager(QAndroidPlatformClipboard *manager) - { - m_manager = manager; - QJniObject::callStaticMethod(applicationClass(), "registerClipboardManager"); - jclass appClass = QtAndroid::applicationClass(); - QJniEnvironment env; - if (env->RegisterNatives(appClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { - __android_log_print(ANDROID_LOG_FATAL,"Qt", "RegisterNatives failed"); - return; - } - } - void clearClipboardData() - { - QJniObject::callStaticMethod(applicationClass(), "clearClipData"); - } - void setClipboardMimeData(QMimeData *data) - { - clearClipboardData(); - if (data->hasUrls()) { - QList urls = data->urls(); - for (const auto &u : std::as_const(urls)) { - QJniObject::callStaticMethod(applicationClass(), - "setClipboardUri", - "(Ljava/lang/String;)V", - QJniObject::fromString(u.toEncoded()).object()); - } - } else if (data->hasHtml()) { // html can contain text - QJniObject::callStaticMethod(applicationClass(), - "setClipboardHtml", - "(Ljava/lang/String;Ljava/lang/String;)V", - QJniObject::fromString(data->text()).object(), - QJniObject::fromString(data->html()).object()); - } else if (data->hasText()) { // hasText must be the last (the order matter here) - QJniObject::callStaticMethod(applicationClass(), - "setClipboardText", "(Ljava/lang/String;)V", - QJniObject::fromString(data->text()).object()); - } - } - - QMimeData *getClipboardMimeData() - { - QMimeData *data = new QMimeData; - if (QJniObject::callStaticMethod(applicationClass(), "hasClipboardText")) { - data->setText(QJniObject::callStaticObjectMethod(applicationClass(), - "getClipboardText", - "()Ljava/lang/String;").toString()); - } - if (QJniObject::callStaticMethod(applicationClass(), "hasClipboardHtml")) { - data->setHtml(QJniObject::callStaticObjectMethod(applicationClass(), - "getClipboardHtml", - "()Ljava/lang/String;").toString()); - } - if (QJniObject::callStaticMethod(applicationClass(), "hasClipboardUri")) { - QJniObject uris = QJniObject::callStaticObjectMethod(applicationClass(), - "getClipboardUris", - "()[Ljava/lang/String;"); - if (uris.isValid()) { - QList urls; - QJniEnvironment env; - jobjectArray juris = uris.object(); - const jint nUris = env->GetArrayLength(juris); - urls.reserve(static_cast(nUris)); - for (int i = 0; i < nUris; ++i) - urls << QUrl(QJniObject(env->GetObjectArrayElement(juris, i)).toString()); - data->setUrls(urls); - } - } - return data; - } - - void onClipboardDataChanged(JNIEnv */*env*/, jobject /*thiz*/) - { - m_manager->emitChanged(QClipboard::Clipboard); - } -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/androidjniclipboard.h b/src/plugins/platforms/android/androidjniclipboard.h deleted file mode 100644 index 24feeef9b3..0000000000 --- a/src/plugins/platforms/android/androidjniclipboard.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2012 BogDan Vatra -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef ANDROIDJNICLIPBOARD_H -#define ANDROIDJNICLIPBOARD_H - -#include -#include "qandroidplatformclipboard.h" -#include "androidjnimain.h" - -QT_BEGIN_NAMESPACE - -class QAndroidPlatformClipboard; -namespace QtAndroidClipboard -{ - // Clipboard support - void setClipboardManager(QAndroidPlatformClipboard *manager); - void setClipboardMimeData(QMimeData *data); - QMimeData *getClipboardMimeData(); - void clearClipboardData(); - void onClipboardDataChanged(JNIEnv */*env*/, jobject /*thiz*/); - // Clipboard support -} - -QT_END_NAMESPACE - -#endif // ANDROIDJNICLIPBOARD_H diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 6b906bfe8c..ebed64d48d 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -10,7 +10,6 @@ #include "androidcontentfileengine.h" #include "androiddeadlockprotector.h" #include "androidjniaccessibility.h" -#include "androidjniclipboard.h" #include "androidjniinput.h" #include "androidjnimain.h" #include "androidjnimenu.h" @@ -18,6 +17,7 @@ #include "qandroideventdispatcher.h" #include "qandroidplatformdialoghelpers.h" #include "qandroidplatformintegration.h" +#include "qandroidplatformclipboard.h" #include #include @@ -927,7 +927,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/) || !QtAndroidInput::registerNatives() || !QtAndroidMenu::registerNatives(env) || !QtAndroidAccessibility::registerNatives(env) - || !QtAndroidDialogHelpers::registerNatives(env)) { + || !QtAndroidDialogHelpers::registerNatives(env) + || !QAndroidPlatformClipboard::registerNatives()) { __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); return -1; } diff --git a/src/plugins/platforms/android/qandroidplatformclipboard.cpp b/src/plugins/platforms/android/qandroidplatformclipboard.cpp index 39a508a1b3..bc199e32fb 100644 --- a/src/plugins/platforms/android/qandroidplatformclipboard.cpp +++ b/src/plugins/platforms/android/qandroidplatformclipboard.cpp @@ -1,15 +1,34 @@ +// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2012 BogDan Vatra // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qandroidplatformclipboard.h" -#include "androidjniclipboard.h" + +#include +#include +#include +#include + #ifndef QT_NO_CLIPBOARD +using namespace QtJniTypes; + QT_BEGIN_NAMESPACE +void QAndroidPlatformClipboard::onClipboardDataChanged(JNIEnv *env, jobject obj, jlong nativePointer) +{ + Q_UNUSED(env) + Q_UNUSED(obj) + + auto *clipboardManager = reinterpret_cast(nativePointer); + if (clipboardManager) + clipboardManager->emitChanged(QClipboard::Clipboard); +} + QAndroidPlatformClipboard::QAndroidPlatformClipboard() { - QtAndroidClipboard::setClipboardManager(this); + m_clipboardManager = QtClipboardManager::construct(QtAndroidPrivate::context(), + reinterpret_cast(this)); } QAndroidPlatformClipboard::~QAndroidPlatformClipboard() @@ -18,24 +37,66 @@ QAndroidPlatformClipboard::~QAndroidPlatformClipboard() delete data; } +QMimeData *QAndroidPlatformClipboard::getClipboardMimeData() +{ + QMimeData *data = new QMimeData; + if (m_clipboardManager.callMethod("hasClipboardText")) { + data->setText(m_clipboardManager.callMethod("getClipboardText")); + } + if (m_clipboardManager.callMethod("hasClipboardHtml")) { + data->setHtml(m_clipboardManager.callMethod("getClipboardHtml")); + } + if (m_clipboardManager.callMethod("hasClipboardUri")) { + auto uris = m_clipboardManager.callMethod("getClipboardUris"); + if (uris.isValid()) { + QList urls; + for (const QString &uri : uris) + urls << QUrl(uri); + data->setUrls(urls); + } + } + return data; +} + QMimeData *QAndroidPlatformClipboard::mimeData(QClipboard::Mode mode) { Q_UNUSED(mode); Q_ASSERT(supportsMode(mode)); if (data) data->deleteLater(); - data = QtAndroidClipboard::getClipboardMimeData(); + data = getClipboardMimeData(); return data; } +void QAndroidPlatformClipboard::clearClipboardData() +{ + m_clipboardManager.callMethod("clearClipData"); +} + +void QAndroidPlatformClipboard::setClipboardMimeData(QMimeData *data) +{ + clearClipboardData(); + auto context = QtAndroidPrivate::context(); + if (data->hasUrls()) { + QList urls = data->urls(); + for (const auto &u : std::as_const(urls)) + m_clipboardManager.callMethod("setClipboardUri", context, u.toEncoded()); + } else if (data->hasHtml()) { // html can contain text + m_clipboardManager.callMethod("setClipboardHtml", + context, data->text(), data->html()); + } else if (data->hasText()) { // hasText must be the last (the order matter here) + m_clipboardManager.callMethod("setClipboardText", context, data->text()); + } +} + void QAndroidPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode) { if (!data) { - QtAndroidClipboard::clearClipboardData(); + clearClipboardData(); return; } if (data && supportsMode(mode)) - QtAndroidClipboard::setClipboardMimeData(data); + setClipboardMimeData(data); if (data != 0) data->deleteLater(); } @@ -45,6 +106,19 @@ bool QAndroidPlatformClipboard::supportsMode(QClipboard::Mode mode) const return QClipboard::Clipboard == mode; } +bool QAndroidPlatformClipboard::registerNatives() +{ + QJniEnvironment env; + bool success = env.registerNativeMethods(Traits::className(), + { Q_JNI_NATIVE_SCOPED_METHOD(onClipboardDataChanged, QAndroidPlatformClipboard) }); + if (!success) { + qCritical() << "QtClipboardManager: registerNativeMethods() failed"; + return false; + } + + return true; +} + QT_END_NAMESPACE #endif // QT_NO_CLIPBOARD diff --git a/src/plugins/platforms/android/qandroidplatformclipboard.h b/src/plugins/platforms/android/qandroidplatformclipboard.h index 1778ca5b28..e3467b83ee 100644 --- a/src/plugins/platforms/android/qandroidplatformclipboard.h +++ b/src/plugins/platforms/android/qandroidplatformclipboard.h @@ -1,3 +1,4 @@ +// Copyright (C) 2023 The Qt Company Ltd. // Copyright (C) 2012 BogDan Vatra // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only @@ -6,8 +7,12 @@ #include #include +#include #ifndef QT_NO_CLIPBOARD + +Q_DECLARE_JNI_CLASS(QtClipboardManager, "org/qtproject/qt/android/QtClipboardManager"); + QT_BEGIN_NAMESPACE class QAndroidPlatformClipboard : public QPlatformClipboard @@ -18,8 +23,19 @@ public: QMimeData *mimeData(QClipboard::Mode mode = QClipboard::Clipboard) override; void setMimeData(QMimeData *data, QClipboard::Mode mode = QClipboard::Clipboard) override; bool supportsMode(QClipboard::Mode mode) const override; + + static bool registerNatives(); + private: + QMimeData *getClipboardMimeData(); + void setClipboardMimeData(QMimeData *data); + void clearClipboardData(); + + static void onClipboardDataChanged(JNIEnv *env, jobject obj, jlong nativePointer); + Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(onClipboardDataChanged) + QMimeData *data = nullptr; + QtJniTypes::QtClipboardManager m_clipboardManager; }; QT_END_NAMESPACE -- cgit v1.2.3