bug 640080 - get/set android clipboard must be done on a thread with a java message loop r=dougt a=blocking-fennec
authorBrad Lassey <blassey@mozilla.com>
Wed, 09 Mar 2011 02:30:56 -0500
changeset 63362 d205dbeada8c3bc3c6ff1649a6f1020d9b58e282
parent 63361 3268032022693d48024c06c721ab88add032e1cb
child 63365 cae1cd4aae738d6702bcc80eef63722e9a184292
push idunknown
push userunknown
push dateunknown
reviewersdougt, blocking-fennec
bugs640080
milestone2.0b13pre
bug 640080 - get/set android clipboard must be done on a thread with a java message loop r=dougt a=blocking-fennec
embedding/android/GeckoAppShell.java
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -100,16 +100,44 @@ public class GeckoAppShell
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadLibs(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
 
+    // A looper thread, accessed by GeckoAppShell.getHandler
+    private static class LooperThread extends Thread {
+        public SynchronousQueue<Handler> mHandlerQueue =
+            new SynchronousQueue<Handler>();
+        
+        public void run() {
+            Looper.prepare();
+            try {
+                mHandlerQueue.put(new Handler());
+            } catch (InterruptedException ie) {}
+            Looper.loop();
+        }
+    }
+
+    private static Handler sHandler = null;
+
+    // Get a Handler for a looper thread, or create one if it doesn't exist yet
+    public static Handler getHandler() {
+        if (sHandler == null) {
+            LooperThread lt = new LooperThread();
+            lt.start();
+            try {
+                sHandler = lt.mHandlerQueue.take();
+            } catch (InterruptedException ie) {}
+        }
+        return sHandler;
+    }
+
     public static File getCacheDir() {
         if (sCacheFile == null)
             sCacheFile = GeckoApp.mAppContext.getCacheDir();
         return sCacheFile;
     }
 
     public static long getFreeSpace() {
         try {
@@ -699,30 +727,48 @@ public class GeckoAppShell
         try {
             GeckoApp.surfaceView.getContext().startActivity(intent);
             return true;
         } catch(ActivityNotFoundException e) {
             return false;
         }
     }
 
+    static SynchronousQueue<String> sClipboardQueue =
+        new SynchronousQueue<String>();
+
+    // On some devices, access to the clipboard service needs to happen
+    // on a thread with a looper, so dispatch this to our looper thread
+    // Note: the main looper won't work because it may be blocked on the
+    // gecko thread, which is most likely this thread
     static String getClipboardText() {
-        Context context = GeckoApp.surfaceView.getContext();
-        ClipboardManager cm = (ClipboardManager)
-            context.getSystemService(Context.CLIPBOARD_SERVICE);
-        if (!cm.hasText())
-            return null;
-        return cm.getText().toString();
+        getHandler().post(new Runnable() { 
+            public void run() {
+                Context context = GeckoApp.surfaceView.getContext();
+                ClipboardManager cm = (ClipboardManager)
+                    context.getSystemService(Context.CLIPBOARD_SERVICE);
+                try {
+                    sClipboardQueue.put(cm.hasText() ?
+                                        cm.getText().toString() : null);
+                } catch (InterruptedException ie) {}
+            }});
+        try {
+            return sClipboardQueue.take();
+        } catch (InterruptedException ie) {}
+        return null;
     }
 
-    static void setClipboardText(String text) {
-        Context context = GeckoApp.surfaceView.getContext();
-        ClipboardManager cm = (ClipboardManager)
-            context.getSystemService(Context.CLIPBOARD_SERVICE);
-        cm.setText(text);
+    static void setClipboardText(final String text) {
+        getHandler().post(new Runnable() { 
+            public void run() {
+                Context context = GeckoApp.surfaceView.getContext();
+                ClipboardManager cm = (ClipboardManager)
+                    context.getSystemService(Context.CLIPBOARD_SERVICE);
+                cm.setText(text);
+            }});
     }
 
     public static void showAlertNotification(String aImageUrl, String aAlertTitle, String aAlertText,
                                              String aAlertCookie, String aAlertName) {
         Log.i("GeckoAppJava", "GeckoAppShell.showAlertNotification\n" +
             "- image = '" + aImageUrl + "'\n" +
             "- title = '" + aAlertTitle + "'\n" +
             "- text = '" + aAlertText +"'\n" +