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>
bb10
Assam Boudjelthia 2023-09-18 12:02:47 +03:00
parent cc921ad104
commit 457566c96f
10 changed files with 329 additions and 306 deletions

View File

@ -28,6 +28,7 @@ set(java_sources
src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java
src/org/qtproject/qt/android/extras/QtNative.java
src/org/qtproject/qt/android/QtConstants.java
src/org/qtproject/qt/android/QtClipboardManager.java
)
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android

View File

@ -0,0 +1,228 @@
// Copyright (C) 2023 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;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import java.util.ArrayList;
import java.util.Objects;
import java.util.concurrent.Semaphore;
public class QtClipboardManager
{
public static native void onClipboardDataChanged(long nativePointer);
private final static String TAG = "QtClipboardManager";
private ClipboardManager m_clipboardManager = null;
private boolean m_usePrimaryClip = false;
private final long m_nativePointer;
public QtClipboardManager(Context context, long nativePointer)
{
m_nativePointer = nativePointer;
registerClipboardManager(context);
}
private void registerClipboardManager(Context context)
{
if (context != null) {
final Semaphore semaphore = new Semaphore(0);
QtNative.runAction(new Runnable() {
@Override
public void run() {
m_clipboardManager =
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (m_clipboardManager != null) {
m_clipboardManager.addPrimaryClipChangedListener(
new ClipboardManager.OnPrimaryClipChangedListener() {
public void onPrimaryClipChanged() {
onClipboardDataChanged(m_nativePointer);
}
});
}
semaphore.release();
}
});
try {
semaphore.acquire();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void clearClipData()
{
if (m_clipboardManager != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
m_clipboardManager.clearPrimaryClip();
} else {
String[] mimeTypes = { ClipDescription.MIMETYPE_UNKNOWN };
ClipData data = new ClipData("", mimeTypes, new ClipData.Item(new Intent()));
m_clipboardManager.setPrimaryClip(data);
}
}
m_usePrimaryClip = false;
}
public void setClipboardText(Context context, String text)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newPlainText("text/plain", text);
updatePrimaryClip(clipData, context);
}
}
public static boolean hasClipboardText(Context context)
{
ClipboardManager clipboardManager =
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if (clipboardManager == null)
return false;
ClipDescription description = clipboardManager.getPrimaryClipDescription();
// getPrimaryClipDescription can fail if the app does not have input focus
if (description == null)
return false;
for (int i = 0; i < description.getMimeTypeCount(); ++i) {
String itemMimeType = description.getMimeType(i);
if (itemMimeType.matches("text/(.*)"))
return true;
}
return false;
}
public boolean hasClipboardText()
{
return hasClipboardMimeType("text/(.*)");
}
public String getClipboardText()
{
try {
if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
ClipData primaryClip = m_clipboardManager.getPrimaryClip();
if (primaryClip != null) {
for (int i = 0; i < primaryClip.getItemCount(); ++i)
if (primaryClip.getItemAt(i).getText() != null)
return primaryClip.getItemAt(i).getText().toString();
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to get clipboard data", e);
}
return "";
}
private void updatePrimaryClip(ClipData clipData, Context context)
{
try {
if (m_usePrimaryClip) {
ClipData clip = m_clipboardManager.getPrimaryClip();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Objects.requireNonNull(clip).addItem(context.getContentResolver(),
clipData.getItemAt(0));
} else {
Objects.requireNonNull(clip).addItem(clipData.getItemAt(0));
}
m_clipboardManager.setPrimaryClip(clip);
} else {
m_clipboardManager.setPrimaryClip(clipData);
m_usePrimaryClip = true;
}
} catch (Exception e) {
Log.e(TAG, "Failed to set clipboard data", e);
}
}
public void setClipboardHtml(Context context, String text, String html)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newHtmlText("text/html", text, html);
updatePrimaryClip(clipData, context);
}
}
private boolean hasClipboardMimeType(String mimeType)
{
if (m_clipboardManager == null)
return false;
ClipDescription description = m_clipboardManager.getPrimaryClipDescription();
// getPrimaryClipDescription can fail if the app does not have input focus
if (description == null)
return false;
for (int i = 0; i < description.getMimeTypeCount(); ++i) {
String itemMimeType = description.getMimeType(i);
if (itemMimeType.matches(mimeType))
return true;
}
return false;
}
public boolean hasClipboardHtml()
{
return hasClipboardMimeType("text/html");
}
public String getClipboardHtml()
{
try {
if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
ClipData primaryClip = m_clipboardManager.getPrimaryClip();
if (primaryClip != null) {
for (int i = 0; i < primaryClip.getItemCount(); ++i)
if (primaryClip.getItemAt(i).getHtmlText() != null)
return primaryClip.getItemAt(i).getHtmlText();
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to get clipboard data", e);
}
return "";
}
public void setClipboardUri(Context context, String uriString)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newUri(context.getContentResolver(), "text/uri-list",
Uri.parse(uriString));
updatePrimaryClip(clipData, context);
}
}
public boolean hasClipboardUri()
{
return hasClipboardMimeType("text/uri-list");
}
private String[] getClipboardUris()
{
ArrayList<String> uris = new ArrayList<>();
try {
if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
ClipData primaryClip = m_clipboardManager.getPrimaryClip();
if (primaryClip != null) {
for (int i = 0; i < primaryClip.getItemCount(); ++i)
if (primaryClip.getItemAt(i).getUri() != null)
uris.add(primaryClip.getItemAt(i).getUri().toString());
}
}
} catch (Exception e) {
Log.e(TAG, "Failed to get clipboard data", e);
}
String[] strings = new String[uris.size()];
strings = uris.toArray(strings);
return strings;
}
}

View File

@ -537,7 +537,7 @@ public class QtInputDelegate {
break;
}
if (!QtNative.hasClipboardText())
if (!QtClipboardManager.hasClipboardText(activity))
editButtons &= ~EditContextView.PASTE_BUTTON;
if ((mode & CursorHandleShowEdit) == CursorHandleShowEdit && editButtons != 0) {

View File

@ -26,9 +26,6 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.system.Os;
import android.content.ClipboardManager;
import android.content.ClipData;
import android.content.ClipDescription;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.ContextMenu;
@ -79,9 +76,7 @@ public class QtNative
private static double m_displayMetricsScaledDensity = 1.0;
private static double m_displayMetricsDensity = 1.0;
private static final int m_moveThreshold = 0;
private static ClipboardManager m_clipboardManager = null;
private static Method m_checkSelfPermissionMethod = null;
private static boolean m_usePrimaryClip = false;
public static QtThread m_qtThread = new QtThread();
@ -525,171 +520,7 @@ public class QtNative
m_activityDelegate.notifyQtAndroidPluginRunning(running);
}
private static void registerClipboardManager()
{
if (m_service == null || m_activity != null) { // Avoid freezing if only service
final Semaphore semaphore = new Semaphore(0);
runAction(new Runnable() {
@Override
public void run() {
if (m_activity != null)
m_clipboardManager = (android.content.ClipboardManager) m_activity.getSystemService(Context.CLIPBOARD_SERVICE);
if (m_clipboardManager != null) {
m_clipboardManager.addPrimaryClipChangedListener(new ClipboardManager.OnPrimaryClipChangedListener() {
public void onPrimaryClipChanged() {
onClipboardDataChanged();
}
});
}
semaphore.release();
}
});
try {
semaphore.acquire();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private static void clearClipData()
{
if (m_clipboardManager != null) {
if (Build.VERSION.SDK_INT >= 28) {
m_clipboardManager.clearPrimaryClip();
} else {
String[] mimeTypes = { ClipDescription.MIMETYPE_UNKNOWN };
ClipData data = new ClipData("", mimeTypes, new ClipData.Item(new Intent()));
m_clipboardManager.setPrimaryClip(data);
}
}
m_usePrimaryClip = false;
}
private static void setClipboardText(String text)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newPlainText("text/plain", text);
updatePrimaryClip(clipData);
}
}
public static boolean hasClipboardText()
{
return hasClipboardMimeType("text/(.*)");
}
private static String getClipboardText()
{
try {
if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
ClipData primaryClip = m_clipboardManager.getPrimaryClip();
for (int i = 0; i < primaryClip.getItemCount(); ++i)
if (primaryClip.getItemAt(i).getText() != null)
return primaryClip.getItemAt(i).getText().toString();
}
} catch (Exception e) {
Log.e(QtTAG, "Failed to get clipboard data", e);
}
return "";
}
private static void updatePrimaryClip(ClipData clipData)
{
try {
if (m_usePrimaryClip) {
ClipData clip = m_clipboardManager.getPrimaryClip();
if (Build.VERSION.SDK_INT >= 26) {
Objects.requireNonNull(clip).addItem(m_activity.getContentResolver(), clipData.getItemAt(0));
} else {
Objects.requireNonNull(clip).addItem(clipData.getItemAt(0));
}
m_clipboardManager.setPrimaryClip(clip);
} else {
m_clipboardManager.setPrimaryClip(clipData);
m_usePrimaryClip = true;
}
} catch (Exception e) {
Log.e(QtTAG, "Failed to set clipboard data", e);
}
}
private static void setClipboardHtml(String text, String html)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newHtmlText("text/html", text, html);
updatePrimaryClip(clipData);
}
}
private static boolean hasClipboardMimeType(String mimeType)
{
if (m_clipboardManager == null)
return false;
ClipDescription description = m_clipboardManager.getPrimaryClipDescription();
// getPrimaryClipDescription can fail if the app does not have input focus
if (description == null)
return false;
for (int i = 0; i < description.getMimeTypeCount(); ++i) {
String itemMimeType = description.getMimeType(i);
if (itemMimeType.matches(mimeType))
return true;
}
return false;
}
public static boolean hasClipboardHtml()
{
return hasClipboardMimeType("text/html");
}
private static String getClipboardHtml()
{
try {
if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
ClipData primaryClip = m_clipboardManager.getPrimaryClip();
for (int i = 0; i < primaryClip.getItemCount(); ++i)
if (primaryClip.getItemAt(i).getHtmlText() != null)
return primaryClip.getItemAt(i).getHtmlText().toString();
}
} catch (Exception e) {
Log.e(QtTAG, "Failed to get clipboard data", e);
}
return "";
}
private static void setClipboardUri(String uriString)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newUri(m_activity.getContentResolver(), "text/uri-list",
Uri.parse(uriString));
updatePrimaryClip(clipData);
}
}
public static boolean hasClipboardUri()
{
return hasClipboardMimeType("text/uri-list");
}
private static String[] getClipboardUris()
{
ArrayList<String> uris = new ArrayList<String>();
try {
if (m_clipboardManager != null && m_clipboardManager.hasPrimaryClip()) {
ClipData primaryClip = m_clipboardManager.getPrimaryClip();
for (int i = 0; i < primaryClip.getItemCount(); ++i)
if (primaryClip.getItemAt(i).getUri() != null)
uris.add(primaryClip.getItemAt(i).getUri().toString());
}
} catch (Exception e) {
Log.e(QtTAG, "Failed to get clipboard data", e);
}
String[] strings = new String[uris.size()];
strings = uris.toArray(strings);
return strings;
}
private static void openContextMenu(final int x, final int y, final int w, final int h)
{
@ -909,10 +740,6 @@ public class QtNative
public static native void onContextMenuClosed(Menu menu);
// menu methods
// clipboard methods
public static native void onClipboardDataChanged();
// clipboard methods
// activity methods
public static native void onActivityResult(int requestCode, int resultCode, Intent data);
public static native void onNewIntent(Intent data);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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