Bug 1403653 - Part 3 - Use the Lightweight Theme accent colour for the status bar in normal mode. r?nechen draft
authorJan Henning <jh+bugzilla@buttercookie.de>
Wed, 27 Sep 2017 21:08:14 +0200
changeset 684470 a1b71965547dbedcfdcddddfb6ad3d2bece15955
parent 684469 49607810d82f26b580484561cc5f2d05c24e68fc
child 736867 fd14dc21684745e12eef400e9e200e844ea84678
push id85620
push usermozilla@buttercookie.de
push dateSun, 22 Oct 2017 13:13:39 +0000
reviewersnechen
bugs1403653
milestone58.0a1
Bug 1403653 - Part 3 - Use the Lightweight Theme accent colour for the status bar in normal mode. r?nechen If the theme doesn't provide an accent colour, we try determining something appropriate ourselves. MozReview-Commit-ID: B5TSeIFsBIK
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java
mobile/android/base/java/org/mozilla/gecko/util/WindowUtil.java
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -108,16 +108,17 @@ import org.mozilla.gecko.home.HomePager.
 import org.mozilla.gecko.home.HomePanelsManager;
 import org.mozilla.gecko.home.HomeScreen;
 import org.mozilla.gecko.home.SearchEngine;
 import org.mozilla.gecko.icons.Icons;
 import org.mozilla.gecko.icons.IconsHelper;
 import org.mozilla.gecko.icons.decoders.FaviconDecoder;
 import org.mozilla.gecko.icons.decoders.IconDirectoryEntry;
 import org.mozilla.gecko.icons.decoders.LoadFaviconResult;
+import org.mozilla.gecko.lwt.LightweightTheme;
 import org.mozilla.gecko.media.VideoPlayer;
 import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
 import org.mozilla.gecko.mma.MmaDelegate;
 import org.mozilla.gecko.mozglue.GeckoLoader;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.notifications.NotificationHelper;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
@@ -179,30 +180,30 @@ import java.lang.reflect.Method;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
-import java.util.Set;
 import java.util.regex.Pattern;
 
 import static org.mozilla.gecko.mma.MmaDelegate.NEW_TAB;
 
 public class BrowserApp extends GeckoApp
                         implements ActionModePresenter,
                                    AnchoredPopup.OnVisibilityChangeListener,
                                    BookmarkEditFragment.Callbacks,
                                    BrowserSearch.OnEditSuggestionListener,
                                    BrowserSearch.OnSearchListener,
                                    DynamicToolbarAnimator.MetricsListener,
                                    DynamicToolbarAnimator.ToolbarChromeProxy,
                                    LayoutInflater.Factory,
+                                   LightweightTheme.OnChangeListener,
                                    OnUrlOpenListener,
                                    OnUrlOpenInBackgroundListener,
                                    PropertyAnimator.PropertyAnimationListener,
                                    TabsPanel.TabsLayoutChangeListener,
                                    View.OnKeyListener {
     private static final String LOGTAG = "GeckoBrowserApp";
 
     private static final int TABS_ANIMATION_DURATION = 450;
@@ -636,17 +637,18 @@ public class BrowserApp extends GeckoApp
             // Kick off download of app content as early as possible so that in the best case it's
             // available before the user starts using the browser.
             DownloadContentService.startStudy(this);
         }
 
         // This has to be prepared prior to calling GeckoApp.onCreate, because
         // widget code and BrowserToolbar need it, and they're created by the
         // layout, which GeckoApp takes care of.
-        ((GeckoApplication) getApplication()).prepareLightweightTheme();
+        final GeckoApplication app = (GeckoApplication) getApplication();
+        app.prepareLightweightTheme();
 
         super.onCreate(savedInstanceState);
 
         initSwitchboard(this, intent, isInAutomation);
         initTelemetryUploader(isInAutomation);
 
         mBrowserChrome = (ViewGroup) findViewById(R.id.browser_chrome);
         mActionBarFlipper = (ViewFlipper) findViewById(R.id.browser_actionbar);
@@ -671,16 +673,18 @@ public class BrowserApp extends GeckoApp
             }
 
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 return false;
             }
         });
 
+        app.getLightweightTheme().addListener(this);
+
         mProgressView = (AnimatedProgressBar) findViewById(R.id.page_progress);
         mDynamicToolbar.setLayerView(mLayerView);
         mProgressView.setDynamicToolbar(mDynamicToolbar);
         mBrowserToolbar.setProgressBar(mProgressView);
 
         // Initialize Tab History Controller.
         tabHistoryController = new TabHistoryController(new OnShowTabHistory() {
             @Override
@@ -1546,16 +1550,19 @@ public class BrowserApp extends GeckoApp
         }
 
         if (mProgressView != null) {
             mProgressView.setDynamicToolbar(null);
         }
 
         mDynamicToolbar.destroy();
 
+        final GeckoApplication app = (GeckoApplication) getApplication();
+        app.getLightweightTheme().removeListener(this);
+
         if (mBrowserToolbar != null)
             mBrowserToolbar.onDestroy();
 
         if (mFindInPageBar != null) {
             mFindInPageBar.onDestroy();
             mFindInPageBar = null;
         }
 
@@ -2325,18 +2332,17 @@ public class BrowserApp extends GeckoApp
         if (mDoorHangerPopup != null) {
             mDoorHangerPopup.enable();
         }
 
         for (final BrowserAppDelegate delegate : delegates) {
             delegate.onTabsTrayHidden(this, mTabsPanel);
         }
 
-        final boolean isPrivate = mBrowserToolbar.isPrivateMode();
-        WindowUtil.setStatusBarColor(this, isPrivate);
+        refreshStatusBarColor();
     }
 
     @Override
     public boolean autoHideTabs() {
         if (areTabsShown()) {
             hideTabs();
             return true;
         }
@@ -4443,9 +4449,24 @@ public class BrowserApp extends GeckoApp
             new EditBookmarkDialog(this).show(pageUrl);
         }
     }
 
     @Override
     public void onEditBookmark(@NonNull Bundle bundle) {
         new EditBookmarkTask(this, bundle).execute();
     }
+
+    @Override
+    public void onLightweightThemeChanged() {
+        refreshStatusBarColor();
+    }
+
+    @Override
+    public void onLightweightThemeReset() {
+        refreshStatusBarColor();
+    }
+
+    private void refreshStatusBarColor() {
+        final boolean isPrivate = mBrowserToolbar.isPrivateMode();
+        WindowUtil.setStatusBarColor(BrowserApp.this, isPrivate);
+    }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java
+++ b/mobile/android/base/java/org/mozilla/gecko/lwt/LightweightTheme.java
@@ -28,16 +28,17 @@ import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.support.annotation.ColorInt;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewParent;
 
 public class LightweightTheme implements BundleEventListener {
     private static final String LOGTAG = "GeckoLightweightTheme";
@@ -45,17 +46,17 @@ public class LightweightTheme implements
     private static final String PREFS_URL = "lightweightTheme.headerURL";
     private static final String PREFS_COLOR = "lightweightTheme.color";
 
     private static final String ASSETS_PREFIX = "resource://android/assets/";
 
     private final Application mApplication;
 
     private Bitmap mBitmap;
-    private int mColor;
+    private @ColorInt int mColor;
     private boolean mIsLight;
 
     public static interface OnChangeListener {
         // The View should change its background/text color.
         public void onLightweightThemeChanged();
 
         // The View should reset to its default background/text color.
         public void onLightweightThemeReset();
@@ -230,18 +231,18 @@ public class LightweightTheme implements
         // The lightweight theme image's width and height.
         final int bitmapWidth = bitmap.getWidth();
         final int bitmapHeight = bitmap.getHeight();
 
         try {
             mColor = Color.parseColor(color);
         } catch (Exception e) {
             // Malformed or missing color.
-            // Default to TRANSPARENT.
-            mColor = Color.TRANSPARENT;
+            // We attempt calculating an accent colour ourselves, falling back to TRANSPARENT.
+            mColor = BitmapUtils.getDominantColor(bitmap, Color.TRANSPARENT);
         }
 
         // Calculate the luminance to determine if it's a light or a dark theme.
         double luminance = (0.2125 * ((mColor & 0x00FF0000) >> 16)) +
                            (0.7154 * ((mColor & 0x0000FF00) >> 8)) +
                            (0.0721 * (mColor & 0x000000FF));
         mIsLight = luminance > 110;
 
@@ -312,16 +313,23 @@ public class LightweightTheme implements
      *
      * @return True if the theme is light.
      */
     public boolean isLightTheme() {
         return mIsLight;
     }
 
     /**
+     * @return The accent color of the theme.
+     */
+    public @ColorInt int getColor() {
+        return mColor;
+    }
+
+    /**
      * Crop the image based on the position of the view on the window.
      * Either the View or one of its ancestors might have scrolled or translated.
      * This value should be taken into account while mapping the View to the Bitmap.
      *
      * @param view The view requesting a cropped bitmap.
      */
     private Bitmap getCroppedBitmap(View view) {
         if (mBitmap == null || view == null) {
--- a/mobile/android/base/java/org/mozilla/gecko/util/WindowUtil.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/WindowUtil.java
@@ -1,50 +1,63 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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 android.app.Activity;
+import android.graphics.Color;
 import android.os.Build;
 import android.support.annotation.ColorInt;
 import android.support.annotation.ColorRes;
 import android.support.v4.content.ContextCompat;
 import android.view.View;
 import android.view.Window;
 
+import org.mozilla.gecko.GeckoApplication;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.lwt.LightweightTheme;
 
 public class WindowUtil {
 
     public static void setTabsTrayStatusBarColor(final Activity activity) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
             return;
         }
         setStatusBarColorRes(activity, R.color.status_bar_bg_color_tabs_tray, true);
     }
 
     public static void setStatusBarColor(final Activity activity, final boolean isPrivate) {
         if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
             return;
         }
 
-        final @ColorRes int colorResId;
+        final @ColorInt int color;
         final boolean isDarkTheme;
 
         if (HardwareUtils.isTablet()) {
-            colorResId = R.color.status_bar_bg_color_tablet;
+            color = ContextCompat.getColor(activity, R.color.status_bar_bg_color_tablet);
             isDarkTheme = true;
         } else {
-            colorResId = isPrivate ? R.color.status_bar_bg_color_private : R.color.status_bar_bg_color;
-            isDarkTheme = isPrivate;
+            LightweightTheme theme = ((GeckoApplication) activity.getApplication()).getLightweightTheme();
+            @ColorInt int themeColor = theme.getColor();
+            if (isPrivate) {
+                color = ContextCompat.getColor(activity, R.color.status_bar_bg_color_private);
+                isDarkTheme = true;
+            } else if (theme.isEnabled() && themeColor != Color.TRANSPARENT) {
+                color = themeColor;
+                isDarkTheme = !theme.isLightTheme();
+            } else {
+                color = ContextCompat.getColor(activity, R.color.status_bar_bg_color);
+                isDarkTheme = false;
+            }
         }
-        setStatusBarColorRes(activity, colorResId, isDarkTheme);
+        setStatusBarColor(activity, color, isDarkTheme);
     }
 
     public static void setStatusBarColorRes(final Activity activity, final @ColorRes int colorResId,
                                             final boolean isDarkTheme) {
         final int backgroundColor = ContextCompat.getColor(activity, colorResId);
         setStatusBarColor(activity, backgroundColor, isDarkTheme);
     }