Updating fallback activity

> Wallpaper based theme support
> Light/dark system UI
> Swipe gestures to start and dismiss a task
> Fixing insets and task preview size

Bug: 75979063
Change-Id: Id402e6ac50551a7c0849742e3a0e77df3ead5aa2
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index 8b28597..f62d1d6 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -48,7 +48,16 @@
         It is set to true so that the activity can be started from command line -->
         <activity android:name="com.android.quickstep.RecentsActivity"
             android:exported="true"
-            android:excludeFromRecents="true" />
+            android:excludeFromRecents="true"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:stateNotNeeded="true"
+            android:theme="@style/LauncherTheme"
+            android:screenOrientation="unspecified"
+            android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
+            android:resizeableActivity="true"
+            android:resumeWhilePausing="true"
+            android:taskAffinity="" />
 
         <!-- Content provider to settings search -->
         <provider
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/res/layout/fallback_recents_activity.xml
index b3fc4ed..22f8b55 100644
--- a/quickstep/res/layout/fallback_recents_activity.xml
+++ b/quickstep/res/layout/fallback_recents_activity.xml
@@ -13,13 +13,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<com.android.quickstep.RecentsRootView
+<com.android.quickstep.fallback.RecentsRootView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/drag_layer"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:fitsSystemWindows="true">
 
-    <com.android.quickstep.FallbackRecentsView
+    <com.android.quickstep.fallback.FallbackRecentsView
         xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/overview_panel"
         android:layout_width="match_parent"
@@ -28,4 +29,4 @@
         android:clipToPadding="false"
         android:theme="@style/HomeScreenElementTheme" />
 
-</com.android.quickstep.RecentsRootView>
\ No newline at end of file
+</com.android.quickstep.fallback.RecentsRootView>
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 369ae3a..7b2487a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
 
 import android.animation.Animator;
@@ -26,15 +25,15 @@
 import android.view.View;
 
 import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
+import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.touch.SwipeDetector;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.util.TouchController;
+import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.PendingAnimation;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
@@ -42,8 +41,8 @@
 /**
  * Touch controller for handling task view card swipes
  */
-public class TaskViewTouchController extends AnimatorListenerAdapter
-        implements TouchController, SwipeDetector.Listener {
+public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
+        extends AnimatorListenerAdapter implements TouchController, SwipeDetector.Listener {
 
     private static final String TAG = "OverviewSwipeController";
 
@@ -53,7 +52,7 @@
     // Progress after which the transition is assumed to be a success in case user does not fling
     private static final float SUCCESS_TRANSITION_PROGRESS = 0.5f;
 
-    private final Launcher mLauncher;
+    protected final T mActivity;
     private final SwipeDetector mDetector;
     private final RecentsView mRecentsView;
     private final int[] mTempCords = new int[2];
@@ -70,10 +69,10 @@
 
     private TaskView mTaskBeingDragged;
 
-    public TaskViewTouchController(Launcher launcher) {
-        mLauncher = launcher;
-        mRecentsView = launcher.getOverviewPanel();
-        mDetector = new SwipeDetector(launcher, this, SwipeDetector.VERTICAL);
+    public TaskViewTouchController(T activity) {
+        mActivity = activity;
+        mRecentsView = activity.getOverviewPanel();
+        mDetector = new SwipeDetector(activity, this, SwipeDetector.VERTICAL);
     }
 
     private boolean canInterceptTouch() {
@@ -81,12 +80,14 @@
             // If we are already animating from a previous state, we can intercept.
             return true;
         }
-        if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+        if (AbstractFloatingView.getTopOpenView(mActivity) != null) {
             return false;
         }
-        return mLauncher.isInState(OVERVIEW);
+        return isRecentsInteractive();
     }
 
+    protected abstract boolean isRecentsInteractive();
+
     @Override
     public void onAnimationCancel(Animator animation) {
         if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
@@ -115,7 +116,7 @@
                 mTaskBeingDragged = null;
 
                 View view = mRecentsView.getChildAt(mRecentsView.getCurrentPage());
-                if (view instanceof TaskView && mLauncher.getDragLayer().isEventOverView(view, ev)) {
+                if (view instanceof TaskView && mActivity.getDragLayer().isEventOverView(view, ev)) {
                     // The tile can be dragged down to open the task.
                     mTaskBeingDragged = (TaskView) view;
                     directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
@@ -156,9 +157,8 @@
         }
 
         mCurrentAnimationIsGoingUp = goingUp;
-        float range = mLauncher.getAllAppsController().getShiftRange();
-        long maxDuration = (long) (2 * range);
-        DragLayer dl = mLauncher.getDragLayer();
+        BaseDragLayer dl = mActivity.getDragLayer();
+        long maxDuration = (long) (2 * dl.getHeight());
 
         if (goingUp) {
             mPendingAnimation = mRecentsView.createTaskDismissAnimation(mTaskBeingDragged,
@@ -254,7 +254,7 @@
         }
         if (wasSuccess) {
             if (!mCurrentAnimationIsGoingUp) {
-                mLauncher.getUserEventDispatcher().logTaskLaunch(logAction,
+                mActivity.getUserEventDispatcher().logTaskLaunch(logAction,
                         Direction.DOWN, mTaskBeingDragged.getTask().getTopComponent());
             }
         }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index e6d06da..3cae371 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.Utilities.getPrefs;
 import static com.android.quickstep.OverviewInteractionState.KEY_SWIPE_UP_ENABLED;
 import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -41,19 +42,19 @@
             return new TouchController[] {
                     launcher.getDragController(),
                     new LandscapeStatesTouchController(launcher),
-                    new TaskViewTouchController(launcher)};
+                    new LauncherTaskViewcontroller(launcher)};
         }
         if (launcher.getDeviceProfile().isVerticalBarLayout()) {
             return new TouchController[] {
                     launcher.getDragController(),
                     new LandscapeStatesTouchController(launcher),
                     new LandscapeEdgeSwipeController(launcher),
-                    new TaskViewTouchController(launcher)};
+                    new LauncherTaskViewcontroller(launcher)};
         } else {
             return new TouchController[] {
                     launcher.getDragController(),
                     new PortraitStatesTouchController(launcher),
-                    new TaskViewTouchController(launcher)};
+                    new LauncherTaskViewcontroller(launcher)};
         }
     }
 
@@ -101,4 +102,16 @@
             model.onTrimMemory(level);
         }
     }
+
+    private static class LauncherTaskViewcontroller extends TaskViewTouchController<Launcher> {
+
+        public LauncherTaskViewcontroller(Launcher activity) {
+            super(activity);
+        }
+
+        @Override
+        protected boolean isRecentsInteractive() {
+            return mActivity.isInState(OVERVIEW);
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 9e2e5ac..4e3528c 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -41,6 +41,7 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.ViewOnDrawExecutor;
+import com.android.quickstep.fallback.FallbackRecentsView;
 import com.android.quickstep.views.LauncherLayoutListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityOptions.java b/quickstep/src/com/android/quickstep/FallbackActivityOptions.java
index 3a7fb2d..04352c3 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityOptions.java
+++ b/quickstep/src/com/android/quickstep/FallbackActivityOptions.java
@@ -16,13 +16,13 @@
 package com.android.quickstep;
 
 import android.graphics.Rect;
-import android.util.Log;
 
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RecentsAnimationListener;
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
 /**
@@ -39,6 +39,7 @@
     @Override
     public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
             Runnable runnable) {
+        showOpeningTarget(targetCompats);
         DummyRecentsAnimationControllerCompat dummyRecentsAnim =
                 new DummyRecentsAnimationControllerCompat(runnable);
 
@@ -52,6 +53,18 @@
         mListener.onAnimationCanceled();
     }
 
+    private void showOpeningTarget(RemoteAnimationTargetCompat[] targetCompats) {
+        for (RemoteAnimationTargetCompat target : targetCompats) {
+            TransactionCompat t = new TransactionCompat();
+            int layer = target.mode == RemoteAnimationTargetCompat.MODE_CLOSING
+                    ? Integer.MAX_VALUE
+                    : target.prefixOrderIndex;
+            t.setLayer(target.leash, layer);
+            t.show(target.leash);
+            t.apply();
+        }
+    }
+
     private static class DummyRecentsAnimationControllerCompat
             extends RecentsAnimationControllerCompat {
 
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index e579205..cf60fdf 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -26,7 +26,11 @@
 import com.android.launcher3.R;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.fallback.RecentsRootView;
 
 /**
  * A simple activity to show the recently launched tasks
@@ -51,6 +55,10 @@
         mRecentsRootView = findViewById(R.id.drag_layer);
         mFallbackRecentsView = findViewById(R.id.overview_panel);
 
+        mRecentsRootView.setup();
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
         RecentsActivityTracker.onRecentsActivityCreate(this);
     }
 
diff --git a/quickstep/src/com/android/quickstep/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
similarity index 88%
rename from quickstep/src/com/android/quickstep/FallbackRecentsView.java
rename to quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 251e958..7d7f88f 100644
--- a/quickstep/src/com/android/quickstep/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.quickstep;
+package com.android.quickstep.fallback;
 
 import android.content.Context;
 import android.graphics.Canvas;
@@ -23,6 +23,7 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
+import com.android.quickstep.RecentsActivity;
 import com.android.quickstep.views.RecentsView;
 
 public class FallbackRecentsView extends RecentsView<RecentsActivity> implements Insettable {
@@ -73,12 +74,7 @@
     public static void getCenterPageRect(DeviceProfile grid, Context context, Rect outRect) {
         Rect targetPadding = getPadding(grid, context);
         verticalCenter(targetPadding, grid);
-        Rect insets = grid.getInsets();
-        outRect.set(
-                targetPadding.left + insets.left,
-                targetPadding.top + insets.top,
-                grid.widthPx - targetPadding.right - insets.right,
-                grid.heightPx - targetPadding.bottom - insets.bottom);
+        getPageRect(grid, context, outRect, targetPadding);
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/RecentsRootView.java b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
similarity index 78%
rename from quickstep/src/com/android/quickstep/RecentsRootView.java
rename to quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
index 24785f9..7aaa88c 100644
--- a/quickstep/src/com/android/quickstep/RecentsRootView.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.quickstep;
+package com.android.quickstep.fallback;
 
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -25,15 +25,22 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsActivity;
 
 public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
 
-    private final BaseActivity mActivity;
+    private final RecentsActivity mActivity;
 
     public RecentsRootView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mActivity = BaseActivity.fromContext(context);
-        mControllers = new TouchController[0];
+        mActivity = (RecentsActivity) BaseActivity.fromContext(context);
+        setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                | SYSTEM_UI_FLAG_LAYOUT_STABLE);
+    }
+
+    public void setup() {
+        mControllers = new TouchController[] { new RecentsTaskController(mActivity) };
     }
 
     @TargetApi(23)
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
new file mode 100644
index 0000000..9463cc9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep.fallback;
+
+import com.android.launcher3.uioverrides.TaskViewTouchController;
+import com.android.quickstep.RecentsActivity;
+
+public class RecentsTaskController extends TaskViewTouchController<RecentsActivity> {
+
+    public RecentsTaskController(RecentsActivity activity) {
+        super(activity);
+    }
+
+    @Override
+    protected boolean isRecentsInteractive() {
+        return mActivity.hasWindowFocus();
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index bac8fc7..1c4e511 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -405,7 +405,7 @@
         getPageRect(grid, context, outRect, targetPadding);
     }
 
-    private static void getPageRect(DeviceProfile grid, Context context, Rect outRect,
+    protected static void getPageRect(DeviceProfile grid, Context context, Rect outRect,
             Rect targetPadding) {
         Rect insets = grid.getInsets();
         outRect.set(
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 34819af..4c11fe6 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -34,13 +34,15 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.badge.BadgeInfo;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.dynamicui.WallpaperColorInfo;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.views.BaseDragLayer;
 
 /**
  * Extension of BaseActivity allowing support for drag-n-drop
  */
-public abstract class BaseDraggingActivity extends BaseActivity {
+public abstract class BaseDraggingActivity extends BaseActivity
+        implements WallpaperColorInfo.OnChangeListener {
 
     private static final String TAG = "BaseDraggingActivity";
 
@@ -57,10 +59,38 @@
 
     private OnStartCallback mOnStartCallback;
 
+    private int mThemeRes = R.style.LauncherTheme;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mIsSafeModeEnabled = getPackageManager().isSafeMode();
+
+        // Update theme
+        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
+        wallpaperColorInfo.addOnChangeListener(this);
+        int themeRes = getThemeRes(wallpaperColorInfo);
+        if (themeRes != mThemeRes) {
+            mThemeRes = themeRes;
+            setTheme(themeRes);
+        }
+    }
+
+    @Override
+    public void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo) {
+        if (mThemeRes != getThemeRes(wallpaperColorInfo)) {
+            recreate();
+        }
+    }
+
+    protected int getThemeRes(WallpaperColorInfo wallpaperColorInfo) {
+        if (wallpaperColorInfo.isDark()) {
+            return R.style.LauncherThemeDark;
+        } else if (wallpaperColorInfo.supportsDarkText()) {
+            return R.style.LauncherThemeDarkText;
+        } else {
+            return R.style.LauncherTheme;
+        }
     }
 
     @Override
@@ -203,6 +233,12 @@
         }
     }
 
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        WallpaperColorInfo.getInstance(this).removeOnChangeListener(this);
+    }
+
     public <T extends BaseDraggingActivity> void setOnStartCallback(OnStartCallback<T> callback) {
         mOnStartCallback = callback;
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 056404e..e2f7488 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -135,8 +135,8 @@
 /**
  * Default launcher application.
  */
-public class Launcher extends BaseDraggingActivity implements LauncherExterns, LauncherModel.Callbacks,
-        LauncherProviderChangeListener, WallpaperColorInfo.OnThemeChangeListener {
+public class Launcher extends BaseDraggingActivity
+        implements LauncherExterns, LauncherModel.Callbacks, LauncherProviderChangeListener {
     public static final String TAG = "Launcher";
     static final boolean LOGD = false;
 
@@ -266,10 +266,6 @@
         }
         TraceHelper.beginSection("Launcher-onCreate");
 
-        WallpaperColorInfo wallpaperColorInfo = WallpaperColorInfo.getInstance(this);
-        wallpaperColorInfo.setOnThemeChangeListener(this);
-        overrideTheme(wallpaperColorInfo.isDark(), wallpaperColorInfo.supportsDarkText());
-
         super.onCreate(savedInstanceState);
         TraceHelper.partitionSection("Launcher-onCreate", "super call");
 
@@ -402,23 +398,10 @@
         return mRotationHelper;
     }
 
-    @Override
-    public void onThemeChanged() {
-        recreate();
-    }
-
     public LauncherStateManager getStateManager() {
         return mStateManager;
     }
 
-    protected void overrideTheme(boolean isDark, boolean supportsDarkText) {
-        if (isDark) {
-            setTheme(R.style.LauncherThemeDark);
-        } else if (supportsDarkText) {
-            setTheme(R.style.LauncherThemeDarkText);
-        }
-    }
-
     @Override
     public <T extends View> T findViewById(int id) {
         return mLauncherView.findViewById(id);
@@ -1367,7 +1350,6 @@
         }
 
         TextKeyListener.getInstance().release();
-        WallpaperColorInfo.getInstance(this).setOnThemeChangeListener(null);
 
         LauncherAnimUtils.onDestroyActivity();
 
diff --git a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
index 267e930..32605a2 100644
--- a/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
+++ b/src/com/android/launcher3/dynamicui/WallpaperColorInfo.java
@@ -33,7 +33,8 @@
     private int mSecondaryColor;
     private boolean mIsDark;
     private boolean mSupportsDarkText;
-    private OnThemeChangeListener mOnThemeChangeListener;
+
+    private OnChangeListener[] mTempListeners;
 
     private WallpaperColorInfo(Context context) {
         mWallpaperManager = WallpaperManagerCompat.getInstance(context);
@@ -61,10 +62,8 @@
     @Override
     public void onColorsChanged(WallpaperColorsCompat colors, int which) {
         if ((which & FLAG_SYSTEM) != 0) {
-            boolean wasDarkTheme = mIsDark;
-            boolean didSupportDarkText = mSupportsDarkText;
             update(colors);
-            notifyChange(wasDarkTheme != mIsDark || didSupportDarkText != mSupportsDarkText);
+            notifyChange();
         }
     }
 
@@ -85,10 +84,6 @@
                     & WallpaperColorsCompat.HINT_SUPPORTS_DARK_THEME) > 0 : false;
     }
 
-    public void setOnThemeChangeListener(OnThemeChangeListener onThemeChangeListener) {
-        this.mOnThemeChangeListener = onThemeChangeListener;
-    }
-
     public void addOnChangeListener(OnChangeListener listener) {
         mListeners.add(listener);
     }
@@ -97,23 +92,19 @@
         mListeners.remove(listener);
     }
 
-    public void notifyChange(boolean themeChanged) {
-        if (themeChanged) {
-            if (mOnThemeChangeListener != null) {
-                mOnThemeChangeListener.onThemeChanged();
-            }
-        } else {
-            for (OnChangeListener listener : mListeners) {
-                listener.onExtractedColorsChanged(this);
-            }
+    private void notifyChange() {
+        OnChangeListener[] copy =
+                mTempListeners != null && mTempListeners.length == mListeners.size() ?
+                        mTempListeners : new OnChangeListener[mListeners.size()];
+
+        // Create a new array to avoid concurrent modification when the activity destroys itself.
+        mTempListeners = mListeners.toArray(copy);
+        for (OnChangeListener listener : mTempListeners) {
+            listener.onExtractedColorsChanged(this);
         }
     }
 
     public interface OnChangeListener {
         void onExtractedColorsChanged(WallpaperColorInfo wallpaperColorInfo);
     }
-
-    public interface OnThemeChangeListener {
-        void onThemeChanged();
-    }
 }