Bug 816457: Make sure the Bitmap is accessed in UI thread in LWTheme. [r=mfinkle] DONTBUILD
authorSriram Ramasubramanian <sriram@mozilla.com>
Tue, 26 Feb 2013 11:42:24 -0800
changeset 123190 ecc7941f57acc8a06f7c656150250f61b82ef69c
parent 123189 7addb08b2e931ce2dcec8137678eaca7681ec41a
child 123191 d5ca304d657d35aa5d7a211d2047168d913e1bb8
push id23710
push usersramasubramanian@mozilla.com
push dateWed, 27 Feb 2013 17:43:06 +0000
treeherdermozilla-inbound@ecc7941f57ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle
bugs816457
milestone22.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 816457: Make sure the Bitmap is accessed in UI thread in LWTheme. [r=mfinkle] DONTBUILD
mobile/android/base/LightweightTheme.java
--- a/mobile/android/base/LightweightTheme.java
+++ b/mobile/android/base/LightweightTheme.java
@@ -13,156 +13,153 @@ import org.json.JSONObject;
 import android.app.Application;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.BitmapDrawable;
 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.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);