Bug 816457 - Make sure the Bitmap is accessed in UI thread in LWTheme. r=mfinkle, a=bajaj
authorSriram Ramasubramanian <sriram@mozilla.com>
Tue, 26 Feb 2013 11:42:24 -0800
changeset 127629 ed30d4f43da0d79ed18fdf1d05bb057ba7abbaae
parent 127626 acbe0689b25a1b7fb86bd9dfc8dbe47cd541063d
child 127630 09de427dd00f3ee21503da3f5870b618f858f665
push id2202
push userryanvm@gmail.com
push dateThu, 28 Feb 2013 20:17:27 +0000
treeherdermozilla-beta@ed30d4f43da0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, bajaj
bugs816457
milestone20.0
Bug 816457 - Make sure the Bitmap is accessed in UI thread in LWTheme. r=mfinkle, a=bajaj
mobile/android/base/LightweightTheme.java
--- a/mobile/android/base/LightweightTheme.java
+++ b/mobile/android/base/LightweightTheme.java
@@ -18,157 +18,154 @@ import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.LayerDrawable;
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.os.Build;
+import android.os.Looper;
+import android.os.Handler;
 import android.util.DisplayMetrics;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewParent;
 
+import java.io.InputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLConnection;
-import java.io.InputStream;
 import java.util.List;
 import java.util.ArrayList;
 
 import android.util.Log;
 
 public class LightweightTheme implements GeckoEventListener {
     private static final String LOGTAG = "GeckoLightweightTheme";
 
     private Application mApplication;
+    private Handler mHandler;
+
     private Bitmap mBitmap;
     private int mColor;
     private boolean mIsLight;
 
     public static interface OnChangeListener {
-        // This is the View's default post.
-        // This is required to post the change/rest on UI thread.
-        public boolean post(Runnable action);
-
         // The View should change its background/text color. 
         public void onLightweightThemeChanged();
 
         // The View should reset to its default background/text color.
         public void onLightweightThemeReset();
     }
 
     private List<OnChangeListener> mListeners;
     
     public LightweightTheme(Application application) {
         mApplication = application;
+        mHandler = new Handler(Looper.getMainLooper());
         mListeners = new ArrayList<OnChangeListener>();
 
+        // unregister isn't needed as the lifetime is same as the application.
         GeckoAppShell.getEventDispatcher().registerEventListener("LightweightTheme:Update", this);
         GeckoAppShell.getEventDispatcher().registerEventListener("LightweightTheme:Disable", this);
     }
 
     public void addListener(final OnChangeListener listener) {
         // Don't inform the listeners that attached late.
         // Their onLayout() will take care of them before their onDraw();
         mListeners.add(listener);
     }
 
     public void removeListener(OnChangeListener listener) {
         mListeners.remove(listener);
     }
 
-    public void setLightweightTheme(String headerURL) {
-        try {
-            // Get the image and convert it to a bitmap.
-            URL url = new URL(headerURL);
-            InputStream stream = url.openStream();
-            mBitmap = BitmapFactory.decodeStream(stream);
-            stream.close();
-
-            // The download could be HTTP for previews, so let's be sure we have a bitmap
-            if (mBitmap == null || mBitmap.getWidth() == 0 || mBitmap.getHeight() == 0) {
-                mBitmap = null;
-                return;
-            }
-
-            // To find the dominant color only once, take the bottom 25% of pixels.
-            DisplayMetrics dm = mApplication.getResources().getDisplayMetrics();
-            int maxWidth = Math.max(dm.widthPixels, dm.heightPixels);
-            int height = (int) (mBitmap.getHeight() * 0.25);
-            Bitmap cropped = Bitmap.createBitmap(mBitmap, mBitmap.getWidth() - maxWidth,
-                                                          mBitmap.getHeight() - height, 
-                                                          maxWidth, height);
-            mColor = BitmapUtils.getDominantColor(cropped, false);
-
-            double luminance = (0.2125 * ((mColor & 0x00FF0000) >> 16)) + 
-                               (0.7154 * ((mColor & 0x0000FF00) >> 8)) + 
-                               (0.0721 * (mColor &0x000000FF));
-            mIsLight = (luminance > 110) ? true : false;
-
-            notifyListeners();
-        } catch(java.net.MalformedURLException e) {
-            mBitmap = null;
-        } catch(java.io.IOException e) {
-            mBitmap = null;
-        }
-    }
-
-    public void resetLightweightTheme() {
-        if (mBitmap != null) {
-            // Reset the bitmap.
-            mBitmap = null;
-
-            // Post the reset on the UI thread.
-            for (OnChangeListener listener : mListeners) {
-                 final OnChangeListener oneListener = listener;
-                 oneListener.post(new Runnable() {
-                     @Override
-                     public void run() {
-                         oneListener.onLightweightThemeReset();
-                     }
-                 });
-            }
-        }
-    }
-
-    public void notifyListeners() {
-        if (mBitmap == null)
-            return;
-
-        // Post the change on the UI thread.
-        for (OnChangeListener listener : mListeners) {
-             final OnChangeListener oneListener = listener;
-             oneListener.post(new Runnable() {
-                 @Override
-                 public void run() {
-                     oneListener.onLightweightThemeChanged();
-                 }
-             });
-        }
-    }
-
     @Override
     public void handleMessage(String event, JSONObject message) {
         try {
             if (event.equals("LightweightTheme:Update")) {
                 JSONObject lightweightTheme = message.getJSONObject("data");
                 String headerURL = lightweightTheme.getString("headerURL"); 
                 int mark = headerURL.indexOf('?');
                 if (mark != -1)
                     headerURL = headerURL.substring(0, mark);
-                setLightweightTheme(headerURL);
+                try {
+                    // Get the image and convert it to a bitmap.
+                    URL url = new URL(headerURL);
+                    InputStream stream = url.openStream();
+                    final Bitmap bitmap = BitmapFactory.decodeStream(stream);
+                    stream.close();
+                    mHandler.post(new Runnable() {
+                        public void run() {
+                            setLightweightTheme(bitmap);
+                        }
+                    });
+                } catch(MalformedURLException e) {
+                } catch(IOException e) {
+                }
             } else if (event.equals("LightweightTheme:Disable")) {
-                resetLightweightTheme();
+                mHandler.post(new Runnable() {
+                    public void run() {
+                        resetLightweightTheme();
+                    }
+                });
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
+    /**
+     * Set a new lightweight theme with the given bitmap.
+     * Note: This should be called on the UI thread to restrict accessing the
+     * bitmap to a single thread.
+     *
+     * @param bitmap The bitmap used for the lightweight theme.
+     */
+    private void setLightweightTheme(Bitmap bitmap) {
+        mBitmap = bitmap;
+        if (mBitmap == null || mBitmap.getWidth() == 0 || mBitmap.getHeight() == 0) {
+            mBitmap = null;
+            return;
+        }
+
+        // To find the dominant color only once, take the bottom 25% of pixels.
+        DisplayMetrics dm = mApplication.getResources().getDisplayMetrics();
+        int maxWidth = Math.max(dm.widthPixels, dm.heightPixels);
+        int height = (int) (mBitmap.getHeight() * 0.25);
+        Bitmap cropped = Bitmap.createBitmap(mBitmap, mBitmap.getWidth() - maxWidth,
+                                                      mBitmap.getHeight() - height, 
+                                                      maxWidth, height);
+        mColor = BitmapUtils.getDominantColor(cropped, false);
+
+        double luminance = (0.2125 * ((mColor & 0x00FF0000) >> 16)) + 
+                           (0.7154 * ((mColor & 0x0000FF00) >> 8)) + 
+                           (0.0721 * (mColor &0x000000FF));
+        mIsLight = (luminance > 110) ? true : false;
+
+        for (OnChangeListener listener : mListeners)
+            listener.onLightweightThemeChanged();
+    }
+
+    /**
+     * Reset the lightweight theme.
+     * Note: This should be called on the UI thread to restrict accessing the
+     * bitmap to a single thread.
+     */
+    private void resetLightweightTheme() {
+        if (mBitmap != null) {
+            // Reset the bitmap.
+            mBitmap = null;
+
+            for (OnChangeListener listener : mListeners)
+                listener.onLightweightThemeReset();
+        }
+    }
 
     /**
      * A lightweight theme is enabled only if there is an active bitmap.
      *
      * @return True if the theme is enabled.
      */
     public boolean isEnabled() {
         return (mBitmap != null);