Merge "Set remote animations duration based on animation being run" into ub-launcher3-master
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 662069a..9006831 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 3fd1ba0..3b75001 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -671,7 +671,13 @@
      */
     private ValueAnimator getWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) {
         Rect bounds = new Rect();
-        if (v instanceof BubbleTextView) {
+        boolean isDeepShortcutTextView = v instanceof DeepShortcutTextView
+                && v.getParent() != null && v.getParent() instanceof DeepShortcutView;
+        if (isDeepShortcutTextView) {
+            // Deep shortcut views have their icon drawn in a sibling view.
+            DeepShortcutView view = (DeepShortcutView) v.getParent();
+            mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), bounds);
+        } else if (v instanceof BubbleTextView) {
             ((BubbleTextView) v).getIconBounds(bounds);
         } else {
             mDragLayer.getDescendantRectRelativeToSelf(v, bounds);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index c86c5af..8a5c55a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -16,6 +16,8 @@
 package com.android.launcher3.uioverrides;
 
 import com.android.launcher3.Launcher;
+import com.android.quickstep.QuickScrubController;
+import com.android.quickstep.RecentsView;
 
 /**
  * Extension of overview state used for QuickScrub
@@ -28,7 +30,14 @@
     private static final boolean DEBUG_DIFFERENT_UI = false;
 
     public FastOverviewState(int id) {
-        super(id, STATE_FLAGS);
+        super(id, QuickScrubController.QUICK_SWITCH_START_DURATION, STATE_FLAGS);
+    }
+
+    @Override
+    public void onStateTransitionEnd(Launcher launcher) {
+        super.onStateTransitionEnd(launcher);
+        RecentsView recentsView = launcher.getOverviewPanel();
+        recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub();
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 4dfbf8d..a88369d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -37,11 +37,11 @@
             | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI;
 
     public OverviewState(int id) {
-        this(id, STATE_FLAGS);
+        this(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
     }
 
-    protected OverviewState(int id, int stateFlags) {
-        super(id, ContainerType.TASKSWITCHER, OVERVIEW_TRANSITION_MS, stateFlags);
+    protected OverviewState(int id, int transitionDuration, int stateFlags) {
+        super(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
index 958091b..eb14d60 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewSwipeController.java
@@ -133,8 +133,7 @@
                 mSwipeDownEnabled = true;
 
                 View view = mRecentsView.getChildAt(mRecentsView.getCurrentPage());
-                if (mLauncher.getDragLayer().isEventOverView(view, ev) &&
-                        view instanceof TaskView) {
+                if (view instanceof TaskView && mLauncher.getDragLayer().isEventOverView(view, ev)) {
                     // The tile can be dragged down to open the task.
                     mTaskBeingDragged = (TaskView) view;
                     directionsToDetectScroll = SwipeDetector.DIRECTION_BOTH;
diff --git a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
index 3247312..f875bb7 100644
--- a/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
+++ b/quickstep/src/com/android/quickstep/NormalizedIconLoader.java
@@ -16,13 +16,13 @@
 package com.android.quickstep;
 
 import android.annotation.TargetApi;
+import android.app.ActivityManager.TaskDescription;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.os.Build.VERSION_CODES;
 import android.os.UserHandle;
 import android.util.LruCache;
 import android.util.SparseArray;
@@ -56,7 +56,7 @@
             BitmapInfo info = mDefaultIcons.get(userId);
             if (info == null) {
                 info = getBitmapInfo(Resources.getSystem()
-                        .getDrawable(android.R.drawable.sym_def_app_icon), userId, false);
+                        .getDrawable(android.R.drawable.sym_def_app_icon), userId, 0, false);
                 mDefaultIcons.put(userId, info);
             }
 
@@ -65,26 +65,30 @@
     }
 
     @Override
-    protected Drawable createBadgedDrawable(Drawable drawable, int userId) {
-        return new FastBitmapDrawable(getBitmapInfo(drawable, userId, false));
+    protected Drawable createBadgedDrawable(Drawable drawable, int userId, TaskDescription desc) {
+        return new FastBitmapDrawable(getBitmapInfo(drawable, userId, desc.getPrimaryColor(),
+                false));
     }
 
     private synchronized BitmapInfo getBitmapInfo(Drawable drawable, int userId,
-            boolean isInstantApp) {
+            int primaryColor, boolean isInstantApp) {
         if (mLauncherIcons == null) {
             mLauncherIcons = LauncherIcons.obtain(mContext);
         }
 
+        mLauncherIcons.setWrapperBackgroundColor(primaryColor);
         // User version code O, so that the icon is always wrapped in an adaptive icon container.
         return mLauncherIcons.createBadgedIconBitmap(drawable, UserHandle.of(userId),
                 Build.VERSION_CODES.O, isInstantApp);
     }
 
     @Override
-    protected synchronized Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId) {
+    protected Drawable getBadgedActivityIcon(ActivityInfo activityInfo, int userId,
+            TaskDescription desc) {
         BitmapInfo bitmapInfo = getBitmapInfo(
                 activityInfo.loadUnbadgedIcon(mContext.getPackageManager()),
                 userId,
+                desc.getPrimaryColor(),
                 activityInfo.applicationInfo.isInstantApp());
         return mDrawableFactory.newIcon(bitmapInfo, activityInfo);
     }
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index b9d96b9..0ccd7f2 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -34,8 +34,7 @@
  */
 public class QuickScrubController implements OnAlarmListener {
 
-    public static final int QUICK_SWITCH_START_DURATION = 133;
-    public static final int QUICK_SWITCH_SNAP_DURATION = 120;
+    public static final int QUICK_SWITCH_START_DURATION = 210;
 
     private static final boolean ENABLE_AUTO_ADVANCE = true;
     private static final int NUM_QUICK_SCRUB_SECTIONS = 3;
@@ -52,6 +51,8 @@
     private int mQuickScrubSection;
     private boolean mStartedFromHome;
     private boolean mHasAlarmRun;
+    private boolean mQuickSwitched;
+    private boolean mFinishedTransitionToQuickScrub;
 
     public QuickScrubController(Launcher launcher, RecentsView recentsView) {
         mLauncher = launcher;
@@ -67,6 +68,10 @@
         mStartedFromHome = startingFromHome;
         mQuickScrubSection = 0;
         mHasAlarmRun = false;
+        mQuickSwitched = false;
+        mFinishedTransitionToQuickScrub = false;
+
+        snapToNextTaskIfAvailable();
         mLauncher.getUserEventDispatcher().resetActionDurationMillis();
     }
 
@@ -97,7 +102,9 @@
         int quickScrubSection = Math.round(progress * NUM_QUICK_SCRUB_SECTIONS);
         if (quickScrubSection != mQuickScrubSection) {
             int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
-            goToPageWithHaptic(pageToGoTo);
+            if (mFinishedTransitionToQuickScrub) {
+                goToPageWithHaptic(pageToGoTo);
+            }
             if (ENABLE_AUTO_ADVANCE) {
                 if (quickScrubSection == NUM_QUICK_SCRUB_SECTIONS || quickScrubSection == 0) {
                     mAutoAdvanceAlarm.setAlarm(mHasAlarmRun
@@ -111,36 +118,45 @@
     }
 
     public void onQuickSwitch() {
-        for (int i = 0; i < mRecentsView.getPageCount(); i++) {
-            TaskView taskView = (TaskView) mRecentsView.getPageAt(i);
-            if (taskView.getTask().key.id != mRecentsView.getRunningTaskId()) {
-                Runnable launchTaskRunnable = () -> taskView.launchTask(true);
-                if (mRecentsView.snapToPage(i, QUICK_SWITCH_SNAP_DURATION)) {
-                    // Snap to the new page then launch it
-                    mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
-                } else {
-                    // No need to move page, just launch task directly
-                    launchTaskRunnable.run();
-                }
-                break;
-            }
-        }
-        mLauncher.getUserEventDispatcher().logActionOnControl(Touch.FLING,
-                ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
-                        ContainerType.WORKSPACE : ContainerType.APP);
+        mQuickSwitched = true;
+        quickSwitchIfReady();
     }
 
-    public void snapToPageForCurrentQuickScrubSection() {
-        if (mInQuickScrub) {
-            goToPageWithHaptic(mQuickScrubSection);
+    public void onFinishedTransitionToQuickScrub() {
+        mFinishedTransitionToQuickScrub = true;
+        quickSwitchIfReady();
+    }
+
+    /**
+     * Immediately launches the current task (which we snapped to in onQuickScrubStart) if we've
+     * gotten the onQuickSwitch callback and the transition to quick scrub has completed.
+     */
+    private void quickSwitchIfReady() {
+        if (mQuickSwitched && mFinishedTransitionToQuickScrub) {
+            onQuickScrubEnd();
+            mLauncher.getUserEventDispatcher().logActionOnControl(Touch.FLING,
+                    ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
+                            ContainerType.WORKSPACE : ContainerType.APP);
+        }
+    }
+
+    public void snapToNextTaskIfAvailable() {
+        if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
+            int toPage = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
+            goToPageWithHaptic(toPage, QUICK_SWITCH_START_DURATION);
         }
     }
 
     private void goToPageWithHaptic(int pageToGoTo) {
+        goToPageWithHaptic(pageToGoTo, -1);
+    }
+
+    private void goToPageWithHaptic(int pageToGoTo, int overrideDuration) {
         pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
         if (pageToGoTo != mRecentsView.getNextPage()) {
-            int duration = Math.abs(pageToGoTo - mRecentsView.getNextPage())
-                    * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
+            int duration = overrideDuration > -1 ? overrideDuration
+                    : Math.abs(pageToGoTo - mRecentsView.getNextPage())
+                            * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
             mRecentsView.snapToPage(pageToGoTo, duration);
             mRecentsView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
                     HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 9481390..3cd1179 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -339,7 +339,7 @@
         applyIconScale(false /* animate */);
 
         if (oldChildCount != getChildCount()) {
-            mQuickScrubController.snapToPageForCurrentQuickScrubSection();
+            mQuickScrubController.snapToNextTaskIfAvailable();
         }
     }
 
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 5d0bec9..04740d9 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,9 +21,8 @@
 import static android.view.MotionEvent.ACTION_POINTER_DOWN;
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
-
 import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
-import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_START_DURATION;
+import static com.android.launcher3.LauncherState.NORMAL;
 
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
@@ -45,6 +44,7 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.MainThreadExecutor;
 import com.android.launcher3.R;
 import com.android.launcher3.uioverrides.UiFactory;
@@ -316,13 +316,12 @@
             if (TouchConsumer.isInteractionQuick(interactionType)) {
                 Runnable action = () -> {
                     Runnable onComplete = null;
-                    if (interactionType == INTERACTION_QUICK_SCRUB) {
-                        mQuickScrubController.onQuickScrubStart(true);
-                    } else if (interactionType == INTERACTION_QUICK_SWITCH) {
+                    if (interactionType == INTERACTION_QUICK_SWITCH) {
                         onComplete = mQuickScrubController::onQuickSwitch;
                     }
-                    mLauncher.getStateManager().goToState(FAST_OVERVIEW, true, 0,
-                            QUICK_SWITCH_START_DURATION, onComplete);
+                    LauncherState fromState = mLauncher.getStateManager().getState();
+                    mLauncher.getStateManager().goToState(FAST_OVERVIEW, true, onComplete);
+                    mQuickScrubController.onQuickScrubStart(fromState == NORMAL);
                 };
 
                 if (mLauncher.getWorkspace().runOnOverlayHidden(action)) {
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 41b66a4..950a8ac 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -157,11 +157,6 @@
     }
 
     private void goToState(LauncherState state, boolean animated, long delay,
-            Runnable onCompleteRunnable) {
-        goToState(state, animated, delay, -1, onCompleteRunnable);
-    }
-
-    public void goToState(LauncherState state, boolean animated, long delay, long overrideDuration,
             final Runnable onCompleteRunnable) {
         if (mLauncher.isInState(state)) {
             if (mConfig.mCurrentAnimation == null) {
@@ -208,9 +203,6 @@
         // Since state NORMAL can be reached from multiple states, just assume that the
         // transition plays in reverse and use the same duration as previous state.
         mConfig.duration = state == NORMAL ? mState.transitionDuration : state.transitionDuration;
-        if (overrideDuration > -1) {
-            mConfig.duration = overrideDuration;
-        }
 
         AnimatorSet animation = createAnimationToNewWorkspaceInternal(
                 state, new AnimatorSetBuilder(), onCompleteRunnable);
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 2e9ff23..3b5585b 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -29,11 +29,13 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.PaintFlagsDrawFilter;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.PaintDrawable;
 import android.os.Build;
@@ -60,6 +62,8 @@
  */
 public class LauncherIcons implements AutoCloseable {
 
+    private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
+
     public static final Object sPoolSync = new Object();
     private static LauncherIcons sPool;
 
@@ -84,6 +88,9 @@
      */
     public void recycle() {
         synchronized (sPoolSync) {
+            // Clear any temporary state variables
+            mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
+
             next = sPool;
             sPool = this;
         }
@@ -104,7 +111,9 @@
 
     private IconNormalizer mNormalizer;
     private ShadowGenerator mShadowGenerator;
+
     private Drawable mWrapperIcon;
+    private int mWrapperBackgroundColor = DEFAULT_WRAPPER_BACKGROUND;
 
     // sometimes we store linked lists of these things
     private LauncherIcons next;
@@ -222,6 +231,13 @@
                 Math.min(scale[0], ShadowGenerator.getScaleForBounds(iconBounds)));
     }
 
+    /**
+     * Sets the background color used for wrapped adaptive icon
+     */
+    public void setWrapperBackgroundColor(int color) {
+        mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
+    }
+
     private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, int iconAppTargetSdk,
             RectF outIconBounds, float[] outScale) {
         float scale = 1f;
@@ -240,6 +256,8 @@
                 fsd.setScale(scale);
                 icon = dr;
                 scale = getNormalizer().getScale(icon, outIconBounds, null, null);
+
+                ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
             }
         } else {
             scale = getNormalizer().getScale(icon, outIconBounds, null, null);
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index cbdabf3..1fd7078 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -74,6 +74,9 @@
     /** Maps keys to their corresponding current group key */
     private final Map<String, String> mNotificationGroupKeyMap = new HashMap<>();
 
+    /** The last notification key that was dismissed from launcher UI */
+    private String mLastKeyDismissedByLauncher;
+
     private SettingsObserver mNotificationBadgingObserver;
 
     private final Handler.Callback mWorkerCallback = new Handler.Callback() {
@@ -251,13 +254,25 @@
         }
 
         NotificationGroup notificationGroup = mNotificationGroupMap.get(sbn.getGroupKey());
+        String key = sbn.getKey();
         if (notificationGroup != null) {
-            notificationGroup.removeChildKey(sbn.getKey());
+            notificationGroup.removeChildKey(key);
             if (notificationGroup.isEmpty()) {
-                cancelNotification(notificationGroup.getGroupSummaryKey());
+                if (key.equals(mLastKeyDismissedByLauncher)) {
+                    // Only cancel the group notification if launcher dismissed the last child.
+                    cancelNotification(notificationGroup.getGroupSummaryKey());
+                }
                 mNotificationGroupMap.remove(sbn.getGroupKey());
             }
         }
+        if (key.equals(mLastKeyDismissedByLauncher)) {
+            mLastKeyDismissedByLauncher = null;
+        }
+    }
+
+    public void cancelNotificationFromLauncher(String key) {
+        mLastKeyDismissedByLauncher = key;
+        cancelNotification(key);
     }
 
     @Override
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 335426c..f1b8ec0 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -206,7 +206,7 @@
         if (notificationListener == null) {
             return;
         }
-        notificationListener.cancelNotification(notificationKey);
+        notificationListener.cancelNotificationFromLauncher(notificationKey);
     }
 
     public void setAllWidgets(ArrayList<WidgetListRowEntry> allWidgets) {