1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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;
}
}
|