Merge "Using an official API for opening DWB properties of a task" into ub-launcher3-master
diff --git a/SecondaryDisplayLauncher/Android.mk b/SecondaryDisplayLauncher/Android.mk
index 4fb5eba..7f305bb 100644
--- a/SecondaryDisplayLauncher/Android.mk
+++ b/SecondaryDisplayLauncher/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_STATIC_ANDROID_LIBRARIES := androidx.design_design
+LOCAL_STATIC_ANDROID_LIBRARIES := com.google.android.material_material
 
 LOCAL_STATIC_JAVA_LIBRARIES := LauncherPluginLib
 
diff --git a/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml b/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml
index fbaeac4..20f85b1 100644
--- a/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml
+++ b/SecondaryDisplayLauncher/res/layout/app_picker_layout.xml
@@ -20,7 +20,7 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <android.support.design.circularreveal.cardview.CircularRevealCardView
+    <com.google.android.material.circularreveal.cardview.CircularRevealCardView
         android:id="@+id/FloatingSheet"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
@@ -35,9 +35,9 @@
             android:verticalSpacing="@dimen/app_list_horizontal_spacing"
             android:horizontalSpacing="@dimen/app_list_vertical_spacing"
             android:numColumns="auto_fit" />
-    </android.support.design.circularreveal.cardview.CircularRevealCardView>
+    </com.google.android.material.circularreveal.cardview.CircularRevealCardView>
 
-    <android.support.design.widget.FloatingActionButton
+    <com.google.android.material.floatingactionbutton.FloatingActionButton
         android:id="@+id/FloatingActionButton"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
diff --git a/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java b/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java
index 5e8b402..12fa46b 100644
--- a/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java
+++ b/SecondaryDisplayLauncher/src/com/android/launcher3/SecondaryDisplayLauncher.java
@@ -26,8 +26,8 @@
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.os.Bundle;
-import android.support.design.circularreveal.cardview.CircularRevealCardView;
-import android.support.design.widget.FloatingActionButton;
+import com.google.android.material.circularreveal.cardview.CircularRevealCardView;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/go/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
new file mode 100644
index 0000000..4038c99
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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.launcher3.uioverrides;
+
+/**
+ * State indicating that the Launcher is behind an app. Same as {@link OverviewState} for Go as we
+ * do not support swipe to overview or swipe to home.
+ */
+public final class BackgroundAppState extends OverviewState {
+    public BackgroundAppState(int id) {
+        super(id);
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
new file mode 100644
index 0000000..90360ce
--- /dev/null
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2019 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.launcher3.uioverrides;
+
+/**
+ * Extension of overview state used for QuickScrub. Same as {@link OverviewState} for Go as we do
+ * not support quickscrub.
+ */
+public final class FastOverviewState extends OverviewState {
+    public FastOverviewState(int id) {
+        super(id);
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 283e349..6144d69 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -33,9 +33,6 @@
  */
 public class OverviewState extends LauncherState {
 
-    // TODO: Remove this when we refactor BackgroundAppState
-    protected static final Rect sTempRect = new Rect();
-
     private static final int STATE_FLAGS = FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED
             | FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
 
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
new file mode 100644
index 0000000..e1df3ba
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.launcher3.LauncherState.OVERVIEW;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.graphics.Rect;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.util.TransformedRect;
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for the in-launcher recents. As Go does not support most gestures
+ * from app to overview/home, most of this class is stubbed out.
+ * TODO: Implement the app to overview animation functionality
+ */
+public final class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher>{
+
+    @Override
+    public LayoutListener createLayoutListener(Launcher activity) {
+        // Go does not have draggable task snapshots.
+        return null;
+    }
+
+    @Override
+    public void onQuickInteractionStart(Launcher activity, ActivityManager.RunningTaskInfo taskInfo,
+            boolean activityVisible, TouchInteractionLog touchInteractionLog) {
+        // Go does not have quick interactions.
+    }
+
+    @Override
+    public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
+            Context context) {
+        // Go does not have quick scrub.
+        return 0;
+    }
+
+    @Override
+    public void executeOnWindowAvailable(Launcher activity, Runnable action) {
+        // Go does not support live tiles.
+    }
+
+    @Override
+    public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
+        LauncherState startState = activity.getStateManager().getRestState();
+        activity.getStateManager().goToState(startState, activityVisible);
+    }
+
+    @Override
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
+            int interactionType, TransformedRect outRect) {
+        // TODO Implement outRect depending on where the task should animate to.
+        // Go does not support swipe up gesture.
+        return 0;
+    }
+
+    @Override
+    public void onSwipeUpComplete(Launcher activity) {
+        // Go does not support swipe up gesture.
+    }
+
+    @Override
+    public HomeAnimationFactory prepareHomeUI(Launcher activity) {
+        // Go does not support gestures from app to home.
+        return null;
+    }
+
+    @Override
+    public AnimationFactory prepareRecentsUI(Launcher activity,
+            boolean activityVisible, boolean animateActivity,
+            Consumer<AnimatorPlaybackController> callback) {
+        //TODO: Implement this based off where the recents view needs to be for app => recents anim.
+        return new AnimationFactory() {
+            @Override
+            public void createActivityController(long transitionLength,
+                    @TouchConsumer.InteractionType int interactionType) {}
+
+            @Override
+            public void onTransitionCancelled() {}
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<Launcher, Boolean> onInitListener) {
+        return new LauncherInitListener(onInitListener);
+    }
+
+    @Override
+    public Launcher getCreatedActivity() {
+        LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+        if (app == null) {
+            return null;
+        }
+        return (Launcher) app.getModel().getCallback();
+    }
+
+    private Launcher getVisibleLauncher() {
+        Launcher launcher = getCreatedActivity();
+        return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
+                launcher : null;
+    }
+
+    @Override
+    public IconRecentsView getVisibleRecentsView() {
+        Launcher launcher = getVisibleLauncher();
+        return launcher != null && launcher.getStateManager().getState().overviewUi
+                ? launcher.getOverviewPanel() : null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
+        Launcher launcher = getVisibleLauncher();
+        if (launcher == null) {
+            return false;
+        }
+        if (fromRecentsButton) {
+            launcher.getUserEventDispatcher().logActionCommand(
+                    LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                    getContainerType(),
+                    LauncherLogProto.ContainerType.TASKSWITCHER);
+        }
+        launcher.getStateManager().goToState(OVERVIEW);
+        return true;
+    }
+
+    @Override
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+        return homeBounds;
+    }
+
+    @Override
+    public boolean shouldMinimizeSplitScreen() {
+        return true;
+    }
+
+    @Override
+    public boolean deferStartingActivity(int downHitTarget) {
+        // Go only supports back to overview so we always defer starting activity.
+        return true;
+    }
+
+    @Override
+    public boolean supportsLongSwipe(Launcher activity) {
+        // Go does not support long swipe from the app.
+        return false;
+    }
+
+    @Override
+    public AlphaProperty getAlphaProperty(Launcher activity) {
+        return activity.getDragLayer().getAlphaProperty(DragLayer.ALPHA_INDEX_SWIPE_UP);
+    }
+
+    @Override
+    public LongSwipeHelper getLongSwipeController(Launcher activity, int runningTaskId) {
+        // Go does not support long swipe from the app.
+        return null;
+    }
+
+    @Override
+    public int getContainerType() {
+        final Launcher launcher = getVisibleLauncher();
+        return launcher != null ? launcher.getStateManager().getState().containerType
+                : LauncherLogProto.ContainerType.APP;
+    }
+
+    @Override
+    public boolean isInLiveTileMode() {
+        // Go does not support live tiles.
+        return false;
+    }
+}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
similarity index 100%
rename from quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FastOverviewState.java
similarity index 100%
rename from quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FastOverviewState.java
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
similarity index 94%
rename from quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index e8cc6fb..af25355 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -272,7 +272,8 @@
             Consumer<AnimatorPlaybackController> callback) {
         LauncherState endState = interactionType == INTERACTION_QUICK_SCRUB
                 ? FAST_OVERVIEW : OVERVIEW;
-        if (wasVisible) {
+        if (wasVisible && fromState != BACKGROUND_APP) {
+            // If a translucent app was launched fom launcher, animate launcher states.
             DeviceProfile dp = activity.getDeviceProfile();
             long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
             callback.accept(activity.getStateManager()
@@ -330,14 +331,17 @@
         }
 
         // Setup the clip animation helper source/target rects in the final transformed state
-        // of the recents view (a scale may be applied prior to this animation starting to
-        // line up the side pages during swipe up)
+        // of the recents view (a scale/translationY may be applied prior to this animation
+        // starting to line up the side pages during swipe up)
         float prevRvScale = recentsView.getScaleX();
+        float prevRvTransY = recentsView.getTranslationY();
         float targetRvScale = endState.getOverviewScaleAndTranslationYFactor(launcher)[0];
         SCALE_PROPERTY.set(recentsView, targetRvScale);
+        recentsView.setTranslationY(0);
         ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher);
         clipHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(), null);
         SCALE_PROPERTY.set(recentsView, prevRvScale);
+        recentsView.setTranslationY(prevRvTransY);
 
         if (!clipHelper.getSourceRect().isEmpty() && !clipHelper.getTargetRect().isEmpty()) {
             float fromScale = clipHelper.getSourceRect().width()
@@ -371,7 +375,7 @@
 
     @Nullable
     @UiThread
-    private Launcher getVisibleLaucher() {
+    private Launcher getVisibleLauncher() {
         Launcher launcher = getCreatedActivity();
         return (launcher != null) && launcher.isStarted() && launcher.hasWindowFocus() ?
                 launcher : null;
@@ -380,25 +384,25 @@
     @Nullable
     @Override
     public RecentsView getVisibleRecentsView() {
-        Launcher launcher = getVisibleLaucher();
+        Launcher launcher = getVisibleLauncher();
         return launcher != null && launcher.getStateManager().getState().overviewUi
                 ? launcher.getOverviewPanel() : null;
     }
 
     @Override
     public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
-        Launcher launcher = getVisibleLaucher();
-        if (launcher != null) {
-            if (fromRecentsButton) {
-                launcher.getUserEventDispatcher().logActionCommand(
-                        LauncherLogProto.Action.Command.RECENTS_BUTTON,
-                        getContainerType(),
-                        LauncherLogProto.ContainerType.TASKSWITCHER);
-            }
-            launcher.getStateManager().goToState(OVERVIEW);
-            return true;
+        Launcher launcher = getVisibleLauncher();
+        if (launcher == null) {
+            return false;
         }
-        return false;
+        if (fromRecentsButton) {
+            launcher.getUserEventDispatcher().logActionCommand(
+                    LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                    getContainerType(),
+                    LauncherLogProto.ContainerType.TASKSWITCHER);
+        }
+        launcher.getStateManager().goToState(OVERVIEW);
+        return true;
     }
 
     @Override
@@ -436,7 +440,7 @@
 
     @Override
     public int getContainerType() {
-        final Launcher launcher = getVisibleLaucher();
+        final Launcher launcher = getVisibleLauncher();
         return launcher != null ? launcher.getStateManager().getState().containerType
                 : LauncherLogProto.ContainerType.APP;
     }
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index fcb0f6e..0bdb578 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -24,6 +24,7 @@
 import android.graphics.RectF;
 import android.os.Build;
 import android.os.Handler;
+import android.view.View;
 import android.view.animation.Interpolator;
 
 import androidx.annotation.NonNull;
@@ -87,7 +88,7 @@
 
     @UiThread
     @Nullable
-    RecentsView getVisibleRecentsView();
+    <T extends View> T getVisibleRecentsView();
 
     @UiThread
     boolean switchToRecentsIfVisible(boolean fromRecentsButton);
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
new file mode 100644
index 0000000..e0eeda5
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2019 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;
+
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.View;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.util.TransformedRect;
+import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+/**
+ * Provider for the atomic remote window animation from the app to the overview.
+ *
+ * @param <T> activity that contains the overview
+ */
+final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
+        RemoteAnimationProvider {
+
+    private static final long RECENTS_LAUNCH_DURATION = 250;
+    private static final String TAG = "AppToOverviewAnimationProvider";
+
+    private final ActivityControlHelper<T> mHelper;
+    // The id of the currently running task that is transitioning to overview.
+    private final int mTargetTaskId;
+
+    private T mActivity;
+    private RecentsView mRecentsView;
+
+    AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
+        mHelper = helper;
+        mTargetTaskId = targetTaskId;
+    }
+
+    /**
+     * Callback for when the activity is ready/initialized.
+     *
+     * @param activity the activity that is ready
+     * @param wasVisible true if it was visible before
+     */
+    boolean onActivityReady(T activity, Boolean wasVisible) {
+        activity.<RecentsView>getOverviewPanel().setCurrentTask(mTargetTaskId);
+        AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
+        ActivityControlHelper.AnimationFactory factory =
+                mHelper.prepareRecentsUI(activity, wasVisible,
+                false /* animate activity */, (controller) -> {
+                    controller.dispatchOnStart();
+                    ValueAnimator anim = controller.getAnimationPlayer()
+                            .setDuration(RECENTS_LAUNCH_DURATION);
+                    anim.setInterpolator(FAST_OUT_SLOW_IN);
+                    anim.start();
+                });
+        factory.onRemoteAnimationReceived(null);
+        factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL);
+        mActivity = activity;
+        mRecentsView = mActivity.getOverviewPanel();
+        return false;
+    }
+
+    /**
+     * Create remote window animation from the currently running app to the overview panel.
+     *
+     * @param targetCompats the target apps
+     * @return animation from app to overview
+     */
+    @Override
+    public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+        if (mRecentsView != null) {
+            mRecentsView.setRunningTaskIconScaledDown(true);
+        }
+        AnimatorSet anim = new AnimatorSet();
+        anim.addListener(new AnimationSuccessListener() {
+            @Override
+            public void onAnimationSuccess(Animator animator) {
+                if (mRecentsView != null) {
+                    mRecentsView.animateUpRunningTaskIconScale();
+                }
+            }
+        });
+        if (mActivity == null) {
+            Log.e(TAG, "Animation created, before activity");
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+            return anim;
+        }
+
+        RemoteAnimationTargetSet targetSet =
+                new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
+
+        // Use the top closing app to determine the insets for the animation
+        RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mTargetTaskId);
+        if (runningTaskTarget == null) {
+            Log.e(TAG, "No closing app");
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+            return anim;
+        }
+
+        final ClipAnimationHelper clipHelper = new ClipAnimationHelper(mActivity);
+
+        // At this point, the activity is already started and laid-out. Get the home-bounds
+        // relative to the screen using the rootView of the activity.
+        int loc[] = new int[2];
+        View rootView = mActivity.getRootView();
+        rootView.getLocationOnScreen(loc);
+        Rect homeBounds = new Rect(loc[0], loc[1],
+                loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
+        clipHelper.updateSource(homeBounds, runningTaskTarget);
+
+        TransformedRect targetRect = new TransformedRect();
+        mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
+                INTERACTION_NORMAL, targetRect);
+        clipHelper.updateTargetRect(targetRect);
+        clipHelper.prepareAnimation(false /* isOpening */);
+
+        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
+                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
+        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
+        valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
+        valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+        valueAnimator.addUpdateListener((v) -> {
+            params.setProgress((float) v.getAnimatedValue());
+            clipHelper.applyTransform(targetSet, params);
+        });
+
+        if (targetSet.isAnimatingHome()) {
+            // If we are animating home, fade in the opening targets
+            RemoteAnimationTargetSet openingSet =
+                    new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
+
+            TransactionCompat transaction = new TransactionCompat();
+            valueAnimator.addUpdateListener((v) -> {
+                for (RemoteAnimationTargetCompat app : openingSet.apps) {
+                    transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
+                }
+                transaction.apply();
+            });
+        }
+        anim.play(valueAnimator);
+        return anim;
+    }
+
+    /**
+     * Get duration of animation from app to overview.
+     *
+     * @return duration of animation
+     */
+    long getRecentsLaunchDuration() {
+        return RECENTS_LAUNCH_DURATION;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index da4a3de..5b4f673 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -15,44 +15,25 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.systemui.shared.system.ActivityManagerWrapper
         .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
-import android.animation.Animator;
 import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Rect;
 import android.os.Build;
 import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
 import android.view.ViewConfiguration;
 
-import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.logging.UserEventDispatcher;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.ActivityControlHelper.AnimationFactory;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.util.TransformedRect;
 import com.android.quickstep.views.RecentsView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
-import com.android.systemui.shared.system.TransactionCompat;
 
 /**
  * Helper class to handle various atomic commands for switching between Overview.
@@ -60,10 +41,6 @@
 @TargetApi(Build.VERSION_CODES.P)
 public class OverviewCommandHelper {
 
-    private static final long RECENTS_LAUNCH_DURATION = 250;
-
-    private static final String TAG = "OverviewCommandHelper";
-
     private final Context mContext;
     private final ActivityManagerWrapper mAM;
     private final RecentsModel mRecentsModel;
@@ -111,18 +88,17 @@
 
         protected final ActivityControlHelper<T> mHelper;
         private final long mCreateTime;
-        private final int mRunningTaskId;
+        private final AppToOverviewAnimationProvider<T> mAnimationProvider;
 
-        private ActivityInitListener mListener;
-        private T mActivity;
-        private RecentsView mRecentsView;
         private final long mToggleClickedTime = SystemClock.uptimeMillis();
         private boolean mUserEventLogged;
+        private ActivityInitListener mListener;
 
         public RecentsActivityCommand() {
             mHelper = mOverviewComponentObserver.getActivityControlHelper();
             mCreateTime = SystemClock.elapsedRealtime();
-            mRunningTaskId = RecentsModel.getRunningTaskId();
+            mAnimationProvider =
+                    new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId());
 
             // Preload the plan
             mRecentsModel.getTasks(null);
@@ -133,16 +109,21 @@
             long elapsedTime = mCreateTime - mLastToggleTime;
             mLastToggleTime = mCreateTime;
 
-            if (!handleCommand(elapsedTime)) {
-                // Start overview
-                if (!mHelper.switchToRecentsIfVisible(true)) {
-                    mListener = mHelper.createActivityInitListener(this::onActivityReady);
-                    mListener.registerAndStartActivity(
-                            mOverviewComponentObserver.getOverviewIntent(),
-                            this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(),
-                            RECENTS_LAUNCH_DURATION);
-                }
+            if (handleCommand(elapsedTime)) {
+                // Command already handled.
+                return;
             }
+
+            if (mHelper.switchToRecentsIfVisible(true)) {
+                // If successfully switched, then return
+                return;
+            }
+
+            // Otherwise, start overview.
+            mListener = mHelper.createActivityInitListener(this::onActivityReady);
+            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
+                    this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(),
+                    mAnimationProvider.getRecentsLaunchDuration());
         }
 
         protected boolean handleCommand(long elapsedTime) {
@@ -163,26 +144,14 @@
         }
 
         private boolean onActivityReady(T activity, Boolean wasVisible) {
-            activity.<RecentsView>getOverviewPanel().setCurrentTask(mRunningTaskId);
-            AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
-            AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible,
-                    false /* animate activity */, (controller) -> {
-                        controller.dispatchOnStart();
-                        ValueAnimator anim = controller.getAnimationPlayer()
-                                .setDuration(RECENTS_LAUNCH_DURATION);
-                        anim.setInterpolator(FAST_OUT_SLOW_IN);
-                        anim.start();
-                });
-            factory.onRemoteAnimationReceived(null);
-            factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL);
-            mActivity = activity;
-            mRecentsView = mActivity.getOverviewPanel();
             if (!mUserEventLogged) {
-                activity.getUserEventDispatcher().logActionCommand(Action.Command.RECENTS_BUTTON,
-                        mHelper.getContainerType(), ContainerType.TASKSWITCHER);
+                activity.getUserEventDispatcher().logActionCommand(
+                        LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                        mHelper.getContainerType(),
+                        LauncherLogProto.ContainerType.TASKSWITCHER);
                 mUserEventLogged = true;
             }
-            return false;
+            return mAnimationProvider.onActivityReady(activity, wasVisible);
         }
 
         private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
@@ -191,80 +160,9 @@
                         (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
             }
 
-            if (mListener != null) {
-                mListener.unregister();
-            }
-            if (mRecentsView != null) {
-                mRecentsView.setRunningTaskIconScaledDown(true);
-            }
-            AnimatorSet anim = new AnimatorSet();
-            anim.addListener(new AnimationSuccessListener() {
-                @Override
-                public void onAnimationSuccess(Animator animator) {
-                    if (mRecentsView != null) {
-                        mRecentsView.animateUpRunningTaskIconScale();
-                    }
-                }
-            });
-            if (mActivity == null) {
-                Log.e(TAG, "Animation created, before activity");
-                anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
-                return anim;
-            }
+            mListener.unregister();
 
-            RemoteAnimationTargetSet targetSet =
-                    new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
-
-            // Use the top closing app to determine the insets for the animation
-            RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
-            if (runningTaskTarget == null) {
-                Log.e(TAG, "No closing app");
-                anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
-                return anim;
-            }
-
-            final ClipAnimationHelper clipHelper = new ClipAnimationHelper(mActivity);
-
-            // At this point, the activity is already started and laid-out. Get the home-bounds
-            // relative to the screen using the rootView of the activity.
-            int loc[] = new int[2];
-            View rootView = mActivity.getRootView();
-            rootView.getLocationOnScreen(loc);
-            Rect homeBounds = new Rect(loc[0], loc[1],
-                    loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
-            clipHelper.updateSource(homeBounds, runningTaskTarget);
-
-            TransformedRect targetRect = new TransformedRect();
-            mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
-                    INTERACTION_NORMAL, targetRect);
-            clipHelper.updateTargetRect(targetRect);
-            clipHelper.prepareAnimation(false /* isOpening */);
-
-            ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
-                    .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
-            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
-            valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
-            valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-            valueAnimator.addUpdateListener((v) -> {
-                params.setProgress((float) v.getAnimatedValue());
-                clipHelper.applyTransform(targetSet, params);
-            });
-
-            if (targetSet.isAnimatingHome()) {
-                // If we are animating home, fade in the opening targets
-                RemoteAnimationTargetSet openingSet =
-                        new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
-
-                TransactionCompat transaction = new TransactionCompat();
-                valueAnimator.addUpdateListener((v) -> {
-                    for (RemoteAnimationTargetCompat app : openingSet.apps) {
-                        transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
-                    }
-                    transaction.apply();
-                });
-            }
-            anim.play(valueAnimator);
-            return anim;
+            return mAnimationProvider.createWindowAnimation(targetCompats);
         }
     }
 }
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index a99fc0f..c1417dd 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -1164,11 +1164,18 @@
     @UiThread
     private void startNewTask() {
         // Launch the task user scrolled to (mRecentsView.getNextPage()).
-        mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            // We finish recents animation inside launchTask() when live tile is enabled.
             mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false,
                     result -> setStateOnUiThread(STATE_HANDLER_INVALIDATED),
                     mMainThreadHandler);
-        });
+        } else {
+            mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
+                mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false,
+                        result -> setStateOnUiThread(STATE_HANDLER_INVALIDATED),
+                        mMainThreadHandler);
+            });
+        }
         mTouchInteractionLog.finishRecentsAnimation(false);
         doLogGesture(NEW_TASK);
     }
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index c612b05..57400bd 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -32,7 +32,6 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.view.animation.Interpolator;
-
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseDraggingActivity;
@@ -277,7 +276,8 @@
             updateStackBoundsToMultiWindowTaskSize(activity);
         } else {
             mSourceStackBounds.set(mHomeStackBounds);
-            mSourceInsets.set(ttv.getInsets());
+            Rect fallback = dl.getInsets();
+            mSourceInsets.set(ttv.getInsets(fallback));
         }
 
         TransformedRect targetRect = new TransformedRect();
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 20280c1..5925c4c 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -120,7 +120,7 @@
 
     public static final float SPRING_MIN_VISIBLE_CHANGE = 0.001f;
     public static final float SPRING_DAMPING_RATIO = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY;
-    public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_LOW;
+    public static final float SPRING_STIFFNESS = SpringForce.STIFFNESS_MEDIUM;
 
     public static final FloatProperty<RecentsView> CONTENT_ALPHA =
             new FloatProperty<RecentsView>("contentAlpha") {
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index d0289d0..90604ef 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -169,11 +169,11 @@
         return mDimAlpha;
     }
 
-    public Rect getInsets() {
+    public Rect getInsets(Rect fallback) {
         if (mThumbnailData != null) {
             return mThumbnailData.insets;
         }
-        return new Rect();
+        return fallback;
     }
 
     public int getSysUiStatusNavFlags() {
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 5b9b172..ff9dd13 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -44,10 +44,6 @@
 
     private static final String TAG = "BaseDraggingActivity";
 
-    // The Intent extra that defines whether to ignore the launch animation
-    public static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION =
-            "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION";
-
     // When starting an action mode, setting this tag will cause the action mode to be cancelled
     // automatically when user interacts with the launcher.
     public static final Object AUTO_CANCEL_ACTION_MODE = new Object();
@@ -158,14 +154,7 @@
             return false;
         }
 
-        // Only launch using the new animation if the shortcut has not opted out (this is a
-        // private contract between launcher and may be ignored in the future).
-        boolean useLaunchAnimation = (v != null) &&
-                !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
-        Bundle optsBundle = useLaunchAnimation
-                ? getActivityLaunchOptionsAsBundle(v)
-                : null;
-
+        Bundle optsBundle = (v != null) ? getActivityLaunchOptionsAsBundle(v) : null;
         UserHandle user = item == null ? null : item.user;
 
         // Prepare intent
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index ea59fff..5c5f5e4 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -57,6 +57,7 @@
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -172,7 +173,7 @@
             Log.d(TAG, "APPS_PENDING_INSTALL: " + strings
                     + ", removing packages: " + packageNames);
         }
-        if (Utilities.isEmpty(strings)) {
+        if (strings == null || ((Collection) strings).isEmpty()) {
             return;
         }
         Set<String> newStrings = new HashSet<>(strings);
@@ -268,7 +269,7 @@
         HashSet<ShortcutKey> result = new HashSet<>();
 
         Set<String> strings = Utilities.getPrefs(context).getStringSet(APPS_PENDING_INSTALL, null);
-        if (Utilities.isEmpty(strings)) {
+        if (strings == null || ((Collection) strings).isEmpty()) {
             return result;
         }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fc3af7e..73fba4e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -146,6 +146,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.function.Predicate;
 
 import androidx.annotation.Nullable;
 
@@ -1118,7 +1119,7 @@
         }
     };
 
-    public void updateNotificationDots(final Set<PackageUserKey> updatedDots) {
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         mWorkspace.updateNotificationDots(updatedDots);
         mAppsView.getAppsStore().updateNotificationDots(updatedDots);
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index c847120..60dfbb7 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -57,8 +57,6 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.lang.reflect.Method;
-import java.util.Collection;
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -514,47 +512,12 @@
         }
     }
 
-    /**
-     * Returns true if {@param original} contains all entries defined in {@param updates} and
-     * have the same value.
-     * The comparison uses {@link Object#equals(Object)} to compare the values.
-     */
-    public static boolean containsAll(Bundle original, Bundle updates) {
-        for (String key : updates.keySet()) {
-            Object value1 = updates.get(key);
-            Object value2 = original.get(key);
-            if (value1 == null) {
-                if (value2 != null) {
-                    return false;
-                }
-            } else if (!value1.equals(value2)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /** Returns whether the collection is null or empty. */
-    public static boolean isEmpty(Collection c) {
-        return c == null || c.isEmpty();
-    }
-
     public static boolean isBinderSizeError(Exception e) {
         return e.getCause() instanceof TransactionTooLargeException
                 || e.getCause() instanceof DeadObjectException;
     }
 
     /**
-     * Returns a HashSet with a single element. We use this instead of Collections.singleton()
-     * because HashSet ensures all operations, such as remove, are supported.
-     */
-    public static <T> HashSet<T> singletonHashSet(T elem) {
-        HashSet<T> hashSet = new HashSet<>(1);
-        hashSet.add(elem);
-        return hashSet;
-    }
-
-    /**
      * Utility method to post a runnable on the handler, skipping the synchronization barriers.
      */
     public static void postAsyncCallback(Handler handler, Runnable callback) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3438a26..2db6cd9 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -98,6 +98,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -3059,7 +3060,7 @@
         });
     }
 
-    public void updateNotificationDots(final Set<PackageUserKey> updatedDots) {
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         final PackageUserKey packageUserKey = new PackageUserKey(null, null);
         final IntSet folderIds = new IntSet();
         mapOverItems(MAP_RECURSE, new ItemOperator() {
@@ -3067,7 +3068,7 @@
             public boolean evaluate(ItemInfo info, View v) {
                 if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
                     if (!packageUserKey.updateFromItemInfo(info)
-                            || updatedDots.contains(packageUserKey)) {
+                            || updatedDots.test(packageUserKey)) {
                         ((BubbleTextView) v).applyDotState(info, true /* animate */);
                         folderIds.add(info.container);
                     }
diff --git a/src/com/android/launcher3/allapps/AllAppsStore.java b/src/com/android/launcher3/allapps/AllAppsStore.java
index 52d7d28..8e7fec8 100644
--- a/src/com/android/launcher3/allapps/AllAppsStore.java
+++ b/src/com/android/launcher3/allapps/AllAppsStore.java
@@ -31,6 +31,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.function.Consumer;
+import java.util.function.Predicate;
 
 /**
  * A utility class to maintain the collection of all apps.
@@ -122,11 +123,11 @@
         mIconContainers.remove(container);
     }
 
-    public void updateNotificationDots(Set<PackageUserKey> updatedDots) {
+    public void updateNotificationDots(Predicate<PackageUserKey> updatedDots) {
         updateAllIcons((child) -> {
             if (child.getTag() instanceof ItemInfo) {
                 ItemInfo info = (ItemInfo) child.getTag();
-                if (mTempKey.updateFromItemInfo(info) && updatedDots.contains(mTempKey)) {
+                if (mTempKey.updateFromItemInfo(info) && updatedDots.test(mTempKey)) {
                     child.applyDotState(info, true /* animate */);
                 }
             }
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 288d568..9b23f3f 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -73,6 +73,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Predicate;
 
 /**
  * A container for shortcuts to deep links and notifications associated with an app.
@@ -463,10 +464,10 @@
     /**
      * Updates the notification header if the original icon's dot updated.
      */
-    public void updateNotificationHeader(Set<PackageUserKey> updatedDots) {
+    public void updateNotificationHeader(Predicate<PackageUserKey> updatedDots) {
         ItemInfo itemInfo = (ItemInfo) mOriginalIcon.getTag();
         PackageUserKey packageUser = PackageUserKey.fromItemInfo(itemInfo);
-        if (updatedDots.contains(packageUser)) {
+        if (updatedDots.test(packageUser)) {
             updateNotificationHeader();
         }
     }
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 984a03d..f4da858 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -85,7 +85,7 @@
             }
         }
         if (dotShouldBeRefreshed) {
-            mLauncher.updateNotificationDots(Utilities.singletonHashSet(postedPackageUserKey));
+            mLauncher.updateNotificationDots(t -> postedPackageUserKey.equals(t));
         }
     }
 
@@ -97,7 +97,7 @@
             if (oldDotInfo.getNotificationKeys().size() == 0) {
                 mPackageUserToDotInfos.remove(removedPackageUserKey);
             }
-            mLauncher.updateNotificationDots(Utilities.singletonHashSet(removedPackageUserKey));
+            mLauncher.updateNotificationDots(t -> removedPackageUserKey.equals(t));
             trimNotifications(mPackageUserToDotInfos);
         }
     }
@@ -133,7 +133,7 @@
         }
 
         if (!updatedDots.isEmpty()) {
-            mLauncher.updateNotificationDots(updatedDots.keySet());
+            mLauncher.updateNotificationDots(updatedDots::containsKey);
         }
         trimNotifications(updatedDots);
     }
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index 57a458b..c2bae6d 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -159,7 +159,7 @@
                 mQsb.setId(R.id.qsb_widget);
 
                 if (!isInPreviewMode()) {
-                    if (!Utilities.containsAll(AppWidgetManager.getInstance(context)
+                    if (!containsAll(AppWidgetManager.getInstance(context)
                             .getAppWidgetOptions(widgetId), opts)) {
                         mQsb.updateAppWidgetOptions(opts);
                     }
@@ -296,4 +296,24 @@
 
         QsbWidgetHostView newView(Context context);
     }
+
+    /**
+     * Returns true if {@param original} contains all entries defined in {@param updates} and
+     * have the same value.
+     * The comparison uses {@link Object#equals(Object)} to compare the values.
+     */
+    private static boolean containsAll(Bundle original, Bundle updates) {
+        for (String key : updates.keySet()) {
+            Object value1 = updates.get(key);
+            Object value2 = original.get(key);
+            if (value1 == null) {
+                if (value2 != null) {
+                    return false;
+                }
+            } else if (!value1.equals(value2)) {
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 6ba2f40..71bf781 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.views;
 
-import static com.android.launcher3.BaseDraggingActivity.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION;
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
 
 import android.content.Context;
@@ -195,16 +194,12 @@
             return false;
         }
         Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER)
+                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
                 .putExtra(EXTRA_WALLPAPER_OFFSET,
                         launcher.getWorkspace().getWallpaperOffsetForCenterPage());
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
-
         String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
         if (!TextUtils.isEmpty(pickerPackage)) {
             intent.setPackage(pickerPackage);
-        } else {
-            // If there is no target package, use the default intent chooser animation
-            intent.putExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
         }
         return launcher.startActivitySafely(v, intent, null);
     }