Merge "Started converting Launcher to be gesture-stable" into ub-launcher3-master
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
index f875bb7..a4a2e56 100644
--- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
+++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
@@ -42,12 +42,15 @@
 
     private final SparseArray<BitmapInfo> mDefaultIcons = new SparseArray<>();
     private final DrawableFactory mDrawableFactory;
+    private final boolean mDisableColorExtraction;
     private LauncherIcons mLauncherIcons;
 
     public NormalizedIconLoader(Context context, TaskKeyLruCache<Drawable> iconCache,
-            LruCache<ComponentName, ActivityInfo> activityInfoCache) {
+            LruCache<ComponentName, ActivityInfo> activityInfoCache,
+            boolean disableColorExtraction) {
         super(context, iconCache, activityInfoCache);
         mDrawableFactory = DrawableFactory.get(context);
+        mDisableColorExtraction = disableColorExtraction;
     }
 
     @Override
@@ -74,6 +77,9 @@
             int primaryColor, boolean isInstantApp) {
         if (mLauncherIcons == null) {
             mLauncherIcons = LauncherIcons.obtain(mContext);
+            if (mDisableColorExtraction) {
+                mLauncherIcons.disableColorExtraction();
+            }
         }
 
         mLauncherIcons.setWrapperBackgroundColor(primaryColor);
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index fa4e016..a9b7326 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -94,7 +94,9 @@
             protected IconLoader createNewIconLoader(Context context,
                     TaskKeyLruCache<Drawable> iconCache,
                     LruCache<ComponentName, ActivityInfo> activityInfoCache) {
-                return new NormalizedIconLoader(context, iconCache, activityInfoCache);
+                // Disable finding the dominant color since we don't need to use it
+                return new NormalizedIconLoader(context, iconCache, activityInfoCache,
+                        true /* disableColorExtraction */);
             }
         };
         mRecentsTaskLoader.startLoader(mContext);
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index d6635dc..f5fbf80 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -19,7 +19,9 @@
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Process;
 import android.os.StrictMode;
@@ -81,13 +83,33 @@
 
     @Override
     public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+        updateTheme();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateTheme();
+    }
+
+    private void updateTheme() {
+        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
         if (mThemeRes != getThemeRes(wallpaperColorInfo)) {
             recreate();
         }
     }
 
     protected int getThemeRes(WallpaperColorInfo wallpaperColorInfo) {
-        if (wallpaperColorInfo.isDark()) {
+        boolean darkTheme;
+        if (Utilities.ATLEAST_Q) {
+            Configuration configuration = getResources().getConfiguration();
+            int nightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK;
+            darkTheme = nightMode == Configuration.UI_MODE_NIGHT_YES;
+        } else {
+            darkTheme = wallpaperColorInfo.isDark();
+        }
+
+        if (darkTheme) {
             return wallpaperColorInfo.supportsDarkText() ?
                     R.style.AppTheme_Dark_DarkText : R.style.AppTheme_Dark;
         } else {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index a851318..1c12464 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -83,6 +83,10 @@
     private static final Matrix sMatrix = new Matrix();
     private static final Matrix sInverseMatrix = new Matrix();
 
+    public static final boolean ATLEAST_Q = Build.VERSION.CODENAME.length() == 1
+            && Build.VERSION.CODENAME.charAt(0) >= 'Q'
+            && Build.VERSION.CODENAME.charAt(0) <= 'Z';
+
     public static final boolean ATLEAST_P =
             Build.VERSION.SDK_INT >= Build.VERSION_CODES.P;
 
diff --git a/src/com/android/launcher3/graphics/BitmapInfo.java b/src/com/android/launcher3/graphics/BitmapInfo.java
index ab906e2..c905a78 100644
--- a/src/com/android/launcher3/graphics/BitmapInfo.java
+++ b/src/com/android/launcher3/graphics/BitmapInfo.java
@@ -35,9 +35,15 @@
     }
 
     public static BitmapInfo fromBitmap(Bitmap bitmap) {
+        return fromBitmap(bitmap, null);
+    }
+
+    public static BitmapInfo fromBitmap(Bitmap bitmap, ColorExtractor dominantColorExtractor) {
         BitmapInfo info = new BitmapInfo();
         info.icon = bitmap;
-        info.color = ColorExtractor.findDominantColorByHue(bitmap);
+        info.color = dominantColorExtractor != null
+                ? dominantColorExtractor.findDominantColorByHue(bitmap)
+                : 0;
         return info;
     }
 }
diff --git a/src/com/android/launcher3/graphics/ColorExtractor.java b/src/com/android/launcher3/graphics/ColorExtractor.java
index e9d72b7..da5da9c 100644
--- a/src/com/android/launcher3/graphics/ColorExtractor.java
+++ b/src/com/android/launcher3/graphics/ColorExtractor.java
@@ -18,22 +18,32 @@
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.util.SparseArray;
+import java.util.Arrays;
 
 /**
  * Utility class for extracting colors from a bitmap.
  */
 public class ColorExtractor {
 
-    public static int findDominantColorByHue(Bitmap bitmap) {
-        return findDominantColorByHue(bitmap, 20);
+    private final int NUM_SAMPLES = 20;
+    private final float[] mTmpHsv = new float[3];
+    private final float[] mTmpHueScoreHistogram = new float[360];
+    private final int[] mTmpPixels = new int[NUM_SAMPLES];
+    private final SparseArray<Float> mTmpRgbScores = new SparseArray<>();
+
+    /**
+     * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+     * @param bitmap The bitmap to scan
+     */
+    public int findDominantColorByHue(Bitmap bitmap) {
+        return findDominantColorByHue(bitmap, NUM_SAMPLES);
     }
 
     /**
      * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
      * @param bitmap The bitmap to scan
-     * @param samples The approximate max number of samples to use.
      */
-    public static int findDominantColorByHue(Bitmap bitmap, int samples) {
+    public int findDominantColorByHue(Bitmap bitmap, int samples) {
         final int height = bitmap.getHeight();
         final int width = bitmap.getWidth();
         int sampleStride = (int) Math.sqrt((height * width) / samples);
@@ -42,15 +52,18 @@
         }
 
         // This is an out-param, for getting the hsv values for an rgb
-        float[] hsv = new float[3];
+        float[] hsv = mTmpHsv;
+        Arrays.fill(hsv, 0);
 
         // First get the best hue, by creating a histogram over 360 hue buckets,
         // where each pixel contributes a score weighted by saturation, value, and alpha.
-        float[] hueScoreHistogram = new float[360];
+        float[] hueScoreHistogram = mTmpHueScoreHistogram;
+        Arrays.fill(hueScoreHistogram, 0);
         float highScore = -1;
         int bestHue = -1;
 
-        int[] pixels = new int[samples];
+        int[] pixels = mTmpPixels;
+        Arrays.fill(pixels, 0);
         int pixelCount = 0;
 
         for (int y = 0; y < height; y += sampleStride) {
@@ -82,7 +95,8 @@
             }
         }
 
-        SparseArray<Float> rgbScores = new SparseArray<>();
+        SparseArray<Float> rgbScores = mTmpRgbScores;
+        rgbScores.clear();
         int bestColor = 0xff000000;
         highScore = -1;
         // Go back over the RGB colors that match the winning hue,
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 333fe59..0167a1d 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -90,6 +90,7 @@
         synchronized (sPoolSync) {
             // Clear any temporary state variables
             mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+            mDisableColorExtractor = false;
 
             next = sPool;
             sPool = this;
@@ -105,6 +106,8 @@
     private final Context mContext;
     private final Canvas mCanvas;
     private final PackageManager mPm;
+    private final ColorExtractor mColorExtractor;
+    private boolean mDisableColorExtractor;
 
     private final int mFillResIconDpi;
     private final int mIconBitmapSize;
@@ -121,6 +124,7 @@
     private LauncherIcons(Context context) {
         mContext = context.getApplicationContext();
         mPm = mContext.getPackageManager();
+        mColorExtractor = new ColorExtractor();
 
         InvariantDeviceProfile idp = LauncherAppState.getIDP(mContext);
         mFillResIconDpi = idp.fillResIconDpi;
@@ -196,7 +200,7 @@
      * The bitmap is also visually normalized with other icons.
      */
     public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
-            boolean isInstantApp, float [] scale) {
+            boolean isInstantApp, float[] scale) {
         if (scale == null) {
             scale = new float[1];
         }
@@ -223,7 +227,7 @@
         } else {
             result = bitmap;
         }
-        return BitmapInfo.fromBitmap(result);
+        return BitmapInfo.fromBitmap(result, mDisableColorExtractor ? null : mColorExtractor);
     }
 
     /**
@@ -245,6 +249,14 @@
         mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
     }
 
+    /**
+     * Disables the dominant color extraction for all icons loaded through this session (until
+     * this instance is recycled).
+     */
+    public void disableColorExtraction() {
+        mDisableColorExtractor = true;
+    }
+
     private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
             RectF outIconBounds, float[] outScale) {
         float scale = 1f;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
index 4a8bbbd..7529f17 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/dynamicui/WallpaperManagerCompatVL.java
@@ -18,7 +18,6 @@
 import static android.app.WallpaperManager.FLAG_SYSTEM;
 
 import static com.android.launcher3.Utilities.getDevicePrefs;
-import static com.android.launcher3.graphics.ColorExtractor.findDominantColorByHue;
 
 import android.app.WallpaperInfo;
 import android.app.WallpaperManager;
@@ -47,6 +46,7 @@
 import android.util.Pair;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.graphics.ColorExtractor;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -169,6 +169,7 @@
 
         private HandlerThread mWorkerThread;
         private Handler mWorkerHandler;
+        private ColorExtractor mColorExtractor;
 
         @Override
         public void onCreate() {
@@ -176,6 +177,7 @@
             mWorkerThread = new HandlerThread("ColorExtractionService");
             mWorkerThread.start();
             mWorkerHandler = new Handler(mWorkerThread.getLooper());
+            mColorExtractor = new ColorExtractor();
         }
 
         @Override
@@ -258,7 +260,8 @@
             String value = VERSION_PREFIX + wallpaperId;
 
             if (bitmap != null) {
-                int color = findDominantColorByHue(bitmap, MAX_WALLPAPER_EXTRACTION_AREA);
+                int color = mColorExtractor.findDominantColorByHue(bitmap,
+                        MAX_WALLPAPER_EXTRACTION_AREA);
                 value += "," + color;
             }
 
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index 44cdbaf..7f6dd44 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -124,12 +124,8 @@
             assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
         } else {
             // Verify that the widget id is deleted.
-            assertTrue(Wait.atMost(new Condition() {
-                @Override
-                public boolean isTrue() throws Throwable {
-                    return mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null;
-                }
-            }, DEFAULT_ACTIVITY_TIMEOUT));
+            assertTrue(Wait.atMost(() -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
+                    DEFAULT_ACTIVITY_TIMEOUT));
         }
     }
 
@@ -143,8 +139,7 @@
     /**
      * Condition for searching widget id
      */
-    private class WidgetSearchCondition extends Condition
-            implements Workspace.ItemOperator {
+    private class WidgetSearchCondition implements Condition, Workspace.ItemOperator {
 
         @Override
         public boolean isTrue() throws Throwable {
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 0022d19..ec0a051 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -198,7 +198,7 @@
     /**
      * Condition for for an item
      */
-    private class ItemSearchCondition extends Condition {
+    private class ItemSearchCondition implements Condition {
 
         private final ItemOperator mOp;
 
diff --git a/tests/src/com/android/launcher3/util/Condition.java b/tests/src/com/android/launcher3/util/Condition.java
index e9ee67c..78c652a 100644
--- a/tests/src/com/android/launcher3/util/Condition.java
+++ b/tests/src/com/android/launcher3/util/Condition.java
@@ -8,47 +8,36 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-public abstract class Condition {
+public interface Condition {
 
-    public abstract boolean isTrue() throws Throwable;
+    boolean isTrue() throws Throwable;
 
     /**
      * Converts the condition to be run on UI thread.
      */
-    public static Condition runOnUiThread(final Condition condition) {
+    static Condition runOnUiThread(final Condition condition) {
         final MainThreadExecutor executor = new MainThreadExecutor();
-        return new Condition() {
-            @Override
-            public boolean isTrue() throws Throwable {
-                final AtomicBoolean value = new AtomicBoolean(false);
-                final Throwable[] exceptions = new Throwable[1];
-                final CountDownLatch latch = new CountDownLatch(1);
-                executor.execute(new Runnable() {
-                    @Override
-                    public void run() {
-                        try {
-                            value.set(condition.isTrue());
-                        } catch (Throwable e) {
-                            exceptions[0] = e;
-                        }
-
-                    }
-                });
-                latch.await(1, TimeUnit.SECONDS);
-                if (exceptions[0] != null) {
-                    throw exceptions[0];
+        return () -> {
+            final AtomicBoolean value = new AtomicBoolean(false);
+            final Throwable[] exceptions = new Throwable[1];
+            final CountDownLatch latch = new CountDownLatch(1);
+            executor.execute(() -> {
+                try {
+                    value.set(condition.isTrue());
+                } catch (Throwable e) {
+                    exceptions[0] = e;
                 }
-                return value.get();
+
+            });
+            latch.await(1, TimeUnit.SECONDS);
+            if (exceptions[0] != null) {
+                throw exceptions[0];
             }
+            return value.get();
         };
     }
 
-    public static Condition minChildCount(final UiObject2 obj, final int childCount) {
-        return new Condition() {
-            @Override
-            public boolean isTrue() {
-                return obj.getChildCount() >= childCount;
-            }
-        };
+    static Condition minChildCount(final UiObject2 obj, final int childCount) {
+        return () -> obj.getChildCount() >= childCount;
     }
 }