diff options
author | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2023-09-18 12:02:47 +0300 |
---|---|---|
committer | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2023-10-30 16:59:14 +0200 |
commit | 457566c96f420c452c4709e1e1942933c1ea2c5d (patch) | |
tree | 9e973bb95019eb55c44a5d16bad513fed2da24ba /src/plugins/platforms | |
parent | cc921ad10408b03329caf6dcc62a432bf0feae31 (diff) |
Android: Move clipboard management to own class
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ä <tinja.paavoseppa@qt.io>
Diffstat (limited to 'src/plugins/platforms')
6 files changed, 99 insertions, 132 deletions
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 <bogdan@kde.org> -// 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 <QtCore/QUrl> -#include <QtCore/QJniObject> -#include <QtCore/QJniEnvironment> - -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<void>(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<void>(applicationClass(), "clearClipData"); - } - void setClipboardMimeData(QMimeData *data) - { - clearClipboardData(); - if (data->hasUrls()) { - QList<QUrl> urls = data->urls(); - for (const auto &u : std::as_const(urls)) { - QJniObject::callStaticMethod<void>(applicationClass(), - "setClipboardUri", - "(Ljava/lang/String;)V", - QJniObject::fromString(u.toEncoded()).object()); - } - } else if (data->hasHtml()) { // html can contain text - QJniObject::callStaticMethod<void>(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<void>(applicationClass(), - "setClipboardText", "(Ljava/lang/String;)V", - QJniObject::fromString(data->text()).object()); - } - } - - QMimeData *getClipboardMimeData() - { - QMimeData *data = new QMimeData; - if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardText")) { - data->setText(QJniObject::callStaticObjectMethod(applicationClass(), - "getClipboardText", - "()Ljava/lang/String;").toString()); - } - if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardHtml")) { - data->setHtml(QJniObject::callStaticObjectMethod(applicationClass(), - "getClipboardHtml", - "()Ljava/lang/String;").toString()); - } - if (QJniObject::callStaticMethod<jboolean>(applicationClass(), "hasClipboardUri")) { - QJniObject uris = QJniObject::callStaticObjectMethod(applicationClass(), - "getClipboardUris", - "()[Ljava/lang/String;"); - if (uris.isValid()) { - QList<QUrl> urls; - QJniEnvironment env; - jobjectArray juris = uris.object<jobjectArray>(); - const jint nUris = env->GetArrayLength(juris); - urls.reserve(static_cast<int>(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 <bogdan@kde.org> -// 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 <QString> -#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 <android/api-level.h> #include <android/asset_manager_jni.h> @@ -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 <bogdan@kde.org> // 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 <QtCore/QUrl> +#include <QtCore/QJniEnvironment> +#include <QtCore/QJniObject> +#include <QtCore/private/qjnihelpers_p.h> + #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<QAndroidPlatformClipboard *>(nativePointer); + if (clipboardManager) + clipboardManager->emitChanged(QClipboard::Clipboard); +} + QAndroidPlatformClipboard::QAndroidPlatformClipboard() { - QtAndroidClipboard::setClipboardManager(this); + m_clipboardManager = QtClipboardManager::construct(QtAndroidPrivate::context(), + reinterpret_cast<long>(this)); } QAndroidPlatformClipboard::~QAndroidPlatformClipboard() @@ -18,24 +37,66 @@ QAndroidPlatformClipboard::~QAndroidPlatformClipboard() delete data; } +QMimeData *QAndroidPlatformClipboard::getClipboardMimeData() +{ + QMimeData *data = new QMimeData; + if (m_clipboardManager.callMethod<jboolean>("hasClipboardText")) { + data->setText(m_clipboardManager.callMethod<QString>("getClipboardText")); + } + if (m_clipboardManager.callMethod<jboolean>("hasClipboardHtml")) { + data->setHtml(m_clipboardManager.callMethod<QString>("getClipboardHtml")); + } + if (m_clipboardManager.callMethod<jboolean>("hasClipboardUri")) { + auto uris = m_clipboardManager.callMethod<QString[]>("getClipboardUris"); + if (uris.isValid()) { + QList<QUrl> 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<void>("clearClipData"); +} + +void QAndroidPlatformClipboard::setClipboardMimeData(QMimeData *data) +{ + clearClipboardData(); + auto context = QtAndroidPrivate::context(); + if (data->hasUrls()) { + QList<QUrl> urls = data->urls(); + for (const auto &u : std::as_const(urls)) + m_clipboardManager.callMethod<void>("setClipboardUri", context, u.toEncoded()); + } else if (data->hasHtml()) { // html can contain text + m_clipboardManager.callMethod<void>("setClipboardHtml", + context, data->text(), data->html()); + } else if (data->hasText()) { // hasText must be the last (the order matter here) + m_clipboardManager.callMethod<void>("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<QtClipboardManager>::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 <bogdan@kde.org> // 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 <qpa/qplatformclipboard.h> #include <QMimeData> +#include <QtCore/qjnitypes.h> #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 |