summaryrefslogtreecommitdiffstats
path: root/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java')
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java117
1 files changed, 117 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java
new file mode 100644
index 000000000..02b07674f
--- /dev/null
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/Clipboard.java
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.util;
+
+import java.util.concurrent.SynchronousQueue;
+
+import org.mozilla.gecko.annotation.WrapForJNI;
+import org.mozilla.gecko.AppConstants.Versions;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.util.Log;
+
+public final class Clipboard {
+ // Volatile but not synchronized: we don't care about the race condition in
+ // init, because both app contexts will be the same, but we do care about a
+ // thread having a stale null value of mContext.
+ volatile static Context mContext;
+ private final static String LOGTAG = "GeckoClipboard";
+ private final static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>();
+
+ private Clipboard() {
+ }
+
+ public static void init(final Context c) {
+ if (mContext != null) {
+ Log.w(LOGTAG, "Clipboard.init() called twice!");
+ return;
+ }
+ mContext = c.getApplicationContext();
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ public static String getText() {
+ // If we're on the UI thread or the background thread, we have a looper on the thread
+ // and can just call this directly. For any other threads, post the call to the
+ // background thread.
+
+ if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) {
+ return getClipboardTextImpl();
+ }
+
+ ThreadUtils.postToBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ String text = getClipboardTextImpl();
+ try {
+ sClipboardQueue.put(text != null ? text : "");
+ } catch (InterruptedException ie) { }
+ }
+ });
+
+ try {
+ return sClipboardQueue.take();
+ } catch (InterruptedException ie) {
+ return "";
+ }
+ }
+
+ @WrapForJNI(calledFrom = "gecko")
+ public static void setText(final CharSequence text) {
+ ThreadUtils.postToBackgroundThread(new Runnable() {
+ @Override
+ public void run() {
+ // In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager,
+ // which is a subclass of android.text.ClipboardManager.
+ final android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
+ final ClipData clip = ClipData.newPlainText("Text", text);
+ try {
+ cm.setPrimaryClip(clip);
+ } catch (NullPointerException e) {
+ // Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw
+ // a NullPointerException if Samsung's /data/clipboard directory is full.
+ // Fortunately, the text is still successfully copied to the clipboard.
+ }
+ return;
+ }
+ });
+ }
+
+ /**
+ * @return true if the clipboard is nonempty, false otherwise.
+ */
+ @WrapForJNI(calledFrom = "gecko")
+ public static boolean hasText() {
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
+ return cm.hasPrimaryClip();
+ }
+
+ /**
+ * Deletes all text from the clipboard.
+ */
+ @WrapForJNI(calledFrom = "gecko")
+ public static void clearText() {
+ setText(null);
+ }
+
+ /**
+ * On some devices, access to the clipboard service needs to happen
+ * on a thread with a looper, so this function requires a looper is
+ * present on the thread.
+ */
+ @SuppressWarnings("deprecation")
+ static String getClipboardTextImpl() {
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
+ if (cm.hasPrimaryClip()) {
+ ClipData clip = cm.getPrimaryClip();
+ if (clip != null) {
+ ClipData.Item item = clip.getItemAt(0);
+ return item.coerceToText(mContext).toString();
+ }
+ }
+ return null;
+ }
+}