|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 package org.mozilla.gecko.util; |
|
6 |
|
7 import android.content.ClipData; |
|
8 import android.content.Context; |
|
9 import android.os.Build; |
|
10 import android.util.Log; |
|
11 |
|
12 import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI; |
|
13 |
|
14 import java.util.concurrent.SynchronousQueue; |
|
15 |
|
16 public final class Clipboard { |
|
17 private static Context mContext; |
|
18 private final static String LOGTAG = "GeckoClipboard"; |
|
19 private final static SynchronousQueue<String> sClipboardQueue = new SynchronousQueue<String>(); |
|
20 |
|
21 private Clipboard() { |
|
22 } |
|
23 |
|
24 public static void init(final Context c) { |
|
25 if (mContext != null) { |
|
26 Log.w(LOGTAG, "Clipboard.init() called twice!"); |
|
27 return; |
|
28 } |
|
29 mContext = c.getApplicationContext(); |
|
30 } |
|
31 |
|
32 @WrapElementForJNI(stubName = "GetClipboardTextWrapper") |
|
33 public static String getText() { |
|
34 // If we're on the UI thread or the background thread, we have a looper on the thread |
|
35 // and can just call this directly. For any other threads, post the call to the |
|
36 // background thread. |
|
37 |
|
38 if (ThreadUtils.isOnUiThread() || ThreadUtils.isOnBackgroundThread()) { |
|
39 return getClipboardTextImpl(); |
|
40 } |
|
41 |
|
42 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
43 @Override |
|
44 public void run() { |
|
45 String text = getClipboardTextImpl(); |
|
46 try { |
|
47 sClipboardQueue.put(text != null ? text : ""); |
|
48 } catch (InterruptedException ie) {} |
|
49 } |
|
50 }); |
|
51 try { |
|
52 return sClipboardQueue.take(); |
|
53 } catch (InterruptedException ie) { |
|
54 return ""; |
|
55 } |
|
56 } |
|
57 |
|
58 @WrapElementForJNI(stubName = "SetClipboardText") |
|
59 public static void setText(final CharSequence text) { |
|
60 ThreadUtils.postToBackgroundThread(new Runnable() { |
|
61 @Override |
|
62 @SuppressWarnings("deprecation") |
|
63 public void run() { |
|
64 if (Build.VERSION.SDK_INT >= 11) { |
|
65 android.content.ClipboardManager cm = getClipboardManager(mContext); |
|
66 ClipData clip = ClipData.newPlainText("Text", text); |
|
67 try { |
|
68 cm.setPrimaryClip(clip); |
|
69 } catch (NullPointerException e) { |
|
70 // Bug 776223: This is a Samsung clipboard bug. setPrimaryClip() can throw |
|
71 // a NullPointerException if Samsung's /data/clipboard directory is full. |
|
72 // Fortunately, the text is still successfully copied to the clipboard. |
|
73 } |
|
74 } else { |
|
75 android.text.ClipboardManager cm = getDeprecatedClipboardManager(mContext); |
|
76 cm.setText(text); |
|
77 } |
|
78 } |
|
79 }); |
|
80 } |
|
81 |
|
82 /** |
|
83 * Returns true if the clipboard is nonempty, false otherwise. |
|
84 * |
|
85 * @return true if the clipboard is nonempty, false otherwise. |
|
86 */ |
|
87 @WrapElementForJNI |
|
88 public static boolean hasText() { |
|
89 if (Build.VERSION.SDK_INT >= 11) { |
|
90 android.content.ClipboardManager cm = getClipboardManager(mContext); |
|
91 return cm.hasPrimaryClip(); |
|
92 } |
|
93 |
|
94 android.text.ClipboardManager cm = getDeprecatedClipboardManager(mContext); |
|
95 return cm.hasText(); |
|
96 } |
|
97 |
|
98 /** |
|
99 * Deletes all text from the clipboard. |
|
100 */ |
|
101 @WrapElementForJNI |
|
102 public static void clearText() { |
|
103 setText(null); |
|
104 } |
|
105 |
|
106 private static android.content.ClipboardManager getClipboardManager(Context context) { |
|
107 // In API Level 11 and above, CLIPBOARD_SERVICE returns android.content.ClipboardManager, |
|
108 // which is a subclass of android.text.ClipboardManager. |
|
109 return (android.content.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE); |
|
110 } |
|
111 |
|
112 private static android.text.ClipboardManager getDeprecatedClipboardManager(Context context) { |
|
113 return (android.text.ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE); |
|
114 } |
|
115 |
|
116 /* On some devices, access to the clipboard service needs to happen |
|
117 * on a thread with a looper, so this function requires a looper is |
|
118 * present on the thread. */ |
|
119 @SuppressWarnings("deprecation") |
|
120 private static String getClipboardTextImpl() { |
|
121 if (Build.VERSION.SDK_INT >= 11) { |
|
122 android.content.ClipboardManager cm = getClipboardManager(mContext); |
|
123 if (cm.hasPrimaryClip()) { |
|
124 ClipData clip = cm.getPrimaryClip(); |
|
125 if (clip != null) { |
|
126 ClipData.Item item = clip.getItemAt(0); |
|
127 return item.coerceToText(mContext).toString(); |
|
128 } |
|
129 } |
|
130 } else { |
|
131 android.text.ClipboardManager cm = getDeprecatedClipboardManager(mContext); |
|
132 if (cm.hasText()) { |
|
133 return cm.getText().toString(); |
|
134 } |
|
135 } |
|
136 return null; |
|
137 } |
|
138 } |