Android: Add support for setting/getting html and uris from clipboard

This also updates the used API to use ClipData and not the deprecated
ClipboardManager API.

[ChangeLog][Platform Specific Changes][Android] QClipboard now supports
HTML and URI data.

Fixes: QTBUG-47835
Fixes: QTBUG-71503
Change-Id: I43f82bfc63b3d159087c0fb6c840c186a370e20c
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
Andy Shaw 2018-11-02 10:36:20 +01:00
parent 28b2232e78
commit 4aac07d023
5 changed files with 180 additions and 40 deletions

View File

@ -47,6 +47,7 @@ import java.util.concurrent.Semaphore;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ActivityInfo;
@ -57,6 +58,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.content.ClipboardManager;
import android.content.ClipboardManager.OnPrimaryClipChangedListener;
import android.content.ClipData;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
@ -98,7 +100,9 @@ public class QtNative
private static ClipboardManager m_clipboardManager = null;
private static Method m_checkSelfPermissionMethod = null;
private static Boolean m_tabletEventSupported = null;
private static boolean m_usePrimaryClip = false;
public static QtThread m_qtThread = new QtThread();
private static Method m_addItemMethod = null;
private static final Runnable runPendingCppRunnablesRunnable = new Runnable() {
@Override
public void run() {
@ -697,28 +701,135 @@ public class QtNative
}
}
private static void clearClipData()
{
m_usePrimaryClip = false;
}
private static void setClipboardText(String text)
{
if (m_clipboardManager != null)
m_clipboardManager.setText(text);
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newPlainText("text/plain", text);
updatePrimaryClip(clipData);
}
}
public static boolean hasClipboardText()
{
if (m_clipboardManager != null)
return m_clipboardManager.hasText();
else
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 true;
}
return false;
}
private static String getClipboardText()
{
if (m_clipboardManager != null)
return m_clipboardManager.getText().toString();
else
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();
}
return "";
}
private static void updatePrimaryClip(ClipData clipData)
{
if (m_usePrimaryClip) {
ClipData clip = m_clipboardManager.getPrimaryClip();
if (Build.VERSION.SDK_INT >= 26) {
if (m_addItemMethod == null) {
Class[] cArg = new Class[2];
cArg[0] = ContentResolver.class;
cArg[1] = ClipData.Item.class;
try {
m_addItemMethod = m_clipboardManager.getClass().getMethod("addItem", cArg);
} catch (Exception e) {
}
}
}
if (m_addItemMethod != null) {
try {
m_addItemMethod.invoke(m_activity.getContentResolver(), clipData.getItemAt(0));
} catch (Exception e) {
e.printStackTrace();
}
} else {
clip.addItem(clipData.getItemAt(0));
}
m_clipboardManager.setPrimaryClip(clip);
} else {
m_clipboardManager.setPrimaryClip(clipData);
m_usePrimaryClip = true;
}
}
private static void setClipboardHtml(String text, String html)
{
if (m_clipboardManager != null) {
ClipData clipData = ClipData.newHtmlText("text/html", text, html);
updatePrimaryClip(clipData);
}
}
public static boolean hasClipboardHtml()
{
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 true;
}
return false;
}
private static String getClipboardHtml()
{
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();
}
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()
{
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)
return true;
}
return false;
}
private static String[] getClipboardUris()
{
ArrayList<String> uris = new ArrayList<String>();
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());
}
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)
{
runAction(new Runnable() {

View File

@ -38,6 +38,7 @@
****************************************************************************/
#include "androidjniclipboard.h"
#include <QtCore/QUrl>
#include <QtCore/private/qjni_p.h>
QT_BEGIN_NAMESPACE
@ -62,27 +63,60 @@ namespace QtAndroidClipboard
return;
}
}
void setClipboardText(const QString &text)
void setClipboardMimeData(QMimeData *data)
{
QJNIObjectPrivate::callStaticMethod<void>(applicationClass(), "clearClipData");
if (data->hasText()) {
QJNIObjectPrivate::callStaticMethod<void>(applicationClass(),
"setClipboardText",
"setClipboardText", "(Ljava/lang/String;)V",
QJNIObjectPrivate::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());
}
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(text).object());
QJNIObjectPrivate::fromString(u.toEncoded()).object());
}
}
}
bool hasClipboardText()
QMimeData *getClipboardMimeData()
{
return QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(),
"hasClipboardText");
}
QString clipboardText()
{
QJNIObjectPrivate text = QJNIObjectPrivate::callStaticObjectMethod(applicationClass(),
QMimeData *data = new QMimeData;
if (QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(), "hasClipboardText")) {
data->setText(QJNIObjectPrivate::callStaticObjectMethod(applicationClass(),
"getClipboardText",
"()Ljava/lang/String;");
return text.toString();
"()Ljava/lang/String;").toString());
}
if (QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(), "hasClipboardHtml")) {
data->setHtml(QJNIObjectPrivate::callStaticObjectMethod(applicationClass(),
"getClipboardHtml",
"()Ljava/lang/String;").toString());
}
if (QJNIObjectPrivate::callStaticMethod<jboolean>(applicationClass(), "hasClipboardUri")) {
QJNIObjectPrivate uris = QJNIObjectPrivate::callStaticObjectMethod(applicationClass(),
"getClipboardUris",
"()[Ljava/lang/String;");
if (uris.isValid()) {
QList<QUrl> urls;
QJNIEnvironmentPrivate 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());
data->setUrls(urls);
}
}
return data;
}
void onClipboardDataChanged(JNIEnv */*env*/, jobject /*thiz*/)

View File

@ -51,9 +51,8 @@ namespace QtAndroidClipboard
{
// Clipboard support
void setClipboardManager(QAndroidPlatformClipboard *manager);
void setClipboardText(const QString &text);
bool hasClipboardText();
QString clipboardText();
void setClipboardMimeData(QMimeData *data);
QMimeData *getClipboardMimeData();
void onClipboardDataChanged(JNIEnv */*env*/, jobject /*thiz*/);
// Clipboard support
}

View File

@ -52,16 +52,15 @@ QMimeData *QAndroidPlatformClipboard::mimeData(QClipboard::Mode mode)
{
Q_UNUSED(mode);
Q_ASSERT(supportsMode(mode));
m_mimeData.setText(QtAndroidClipboard::hasClipboardText()
? QtAndroidClipboard::clipboardText()
: QString());
return &m_mimeData;
QMimeData *data = QtAndroidClipboard::getClipboardMimeData();
data->setParent(this);
return data;
}
void QAndroidPlatformClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
{
if (supportsMode(mode))
QtAndroidClipboard::setClipboardText(data != 0 && data->hasText() ? data->text() : QString());
if (data && supportsMode(mode))
QtAndroidClipboard::setClipboardMimeData(data);
if (data != 0)
data->deleteLater();
}

View File

@ -46,7 +46,7 @@
#ifndef QT_NO_CLIPBOARD
QT_BEGIN_NAMESPACE
class QAndroidPlatformClipboard: public QPlatformClipboard
class QAndroidPlatformClipboard : public QObject, public QPlatformClipboard
{
public:
QAndroidPlatformClipboard();
@ -54,9 +54,6 @@ 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;
private:
QMimeData m_mimeData;
};
QT_END_NAMESPACE