Merge "Inherit TextAppearance.DeviceDefault for the RRO text font" into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index 628b632..74b96d4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -319,7 +319,7 @@
 LOCAL_PROGUARD_FLAG_FILES := proguard.flags
 LOCAL_PROGUARD_ENABLED := full
 
-LOCAL_PACKAGE_NAME := Launcher3QuickStepGoIconRecents
+LOCAL_PACKAGE_NAME := Launcher3GoIconRecents
 LOCAL_PRIVILEGED_MODULE := true
 LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3 Launcher3QuickStep
 LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.launcher3
diff --git a/SecondaryDisplayLauncher/res/values-ta/strings.xml b/SecondaryDisplayLauncher/res/values-ta/strings.xml
index 2f1262f..6bb054a 100644
--- a/SecondaryDisplayLauncher/res/values-ta/strings.xml
+++ b/SecondaryDisplayLauncher/res/values-ta/strings.xml
@@ -20,6 +20,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="couldnt_launch" msgid="7873588052226763866">"நடவடிக்கையைத் துவக்க இயலவில்லை"</string>
-    <string name="add_app_shortcut" msgid="2756755330707509435">"ஆப்ஸ் குறுக்குவழியைச் சேர்"</string>
+    <string name="add_app_shortcut" msgid="2756755330707509435">"ஆப்ஸ் ஷார்ட்கட்டைச் சேர்"</string>
     <string name="set_wallpaper" msgid="6475195450505435904">"வால்பேப்பரை அமை"</string>
 </resources>
diff --git a/go/quickstep/res/layout/fallback_recents_activity.xml b/go/quickstep/res/layout/fallback_recents_activity.xml
new file mode 100644
index 0000000..653f463
--- /dev/null
+++ b/go/quickstep/res/layout/fallback_recents_activity.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+     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.
+-->
+<com.android.quickstep.fallback.GoRecentsActivityRootView
+    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:fitsSystemWindows="true">
+
+    <include
+        android:id="@+id/overview_panel"
+        layout="@layout/overview_panel"
+        android:clipChildren="false"
+        android:clipToPadding="false"
+        android:outlineProvider="none"
+        android:theme="@style/HomeScreenElementTheme" />
+</com.android.quickstep.fallback.GoRecentsActivityRootView>
diff --git a/go/quickstep/res/layout/icon_recents_root_view.xml b/go/quickstep/res/layout/icon_recents_root_view.xml
index 82d5890..6c50950 100644
--- a/go/quickstep/res/layout/icon_recents_root_view.xml
+++ b/go/quickstep/res/layout/icon_recents_root_view.xml
@@ -18,13 +18,11 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:gravity="center">
-    <!-- TODO(114136250): Remove this temporary placeholder view for Go recents -->
-    <TextView
+    android:orientation="vertical">
+    <androidx.recyclerview.widget.RecyclerView
         xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/recent_task_recycler_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:gravity="center"
-        android:text="Stub!"
-        android:textSize="40sp"/>
+        android:scrollbars="none"/>
 </com.android.quickstep.views.IconRecentsView>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
index 95d8c4e..96b4ae5 100644
--- a/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
+++ b/go/quickstep/src/com/android/launcher3/GoLauncherAppTransitionManagerImpl.java
@@ -29,18 +29,6 @@
     }
 
     @Override
-    protected boolean isQuickSwitchInProgress() {
-        // Go does not support quick scrub.
-        return false;
-    }
-
-    @Override
-    protected ActivityOptions getQuickSwitchActivityOptions() {
-        // Go does not support quick scrub.
-        return null;
-    }
-
-    @Override
     protected void composeRecentsLaunchAnimator(AnimatorSet anim, View v,
             RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
         //TODO: Implement this based off IconRecentsView
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 6144d69..afc8e47 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -50,11 +50,6 @@
     }
 
     @Override
-    public void onStateDisabled(Launcher launcher) {
-        RecentsModel.INSTANCE.get(launcher).resetAssistCache();
-    }
-
-    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
new file mode 100644
index 0000000..f199643
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -0,0 +1,88 @@
+/*
+ * 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 android.animation.AnimatorSet;
+import android.animation.ValueAnimator;
+import android.util.Log;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * 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;
+
+    AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
+        mHelper = helper;
+    }
+
+    /**
+     * 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) {
+        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);
+        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) {
+        //TODO: Implement the overview to app window animation for Go.
+        AnimatorSet anim = new AnimatorSet();
+        anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+        return anim;
+    }
+
+    /**
+     * Get duration of animation from app to overview.
+     *
+     * @return duration of animation
+     */
+    long getRecentsLaunchDuration() {
+        return RECENTS_LAUNCH_DURATION;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
new file mode 100644
index 0000000..abb9242
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -0,0 +1,121 @@
+/*
+ * 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.LINEAR;
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.graphics.Rect;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.IconRecentsView;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for recents when the default launcher is different than the
+ * currently running one and apps should interact with the {@link RecentsActivity} as opposed
+ * to the in-launcher one.
+ */
+public final class FallbackActivityControllerHelper extends
+        GoActivityControlHelper<RecentsActivity> {
+
+    public FallbackActivityControllerHelper() { }
+
+    @Override
+    public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
+            boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+        if (activityVisible) {
+            return (transitionLength) -> { };
+        }
+
+        IconRecentsView rv = activity.getOverviewPanel();
+        rv.setAlpha(0);
+
+        return new AnimationFactory() {
+
+            boolean isAnimatingToRecents = false;
+
+            @Override
+            public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+                isAnimatingToRecents = targets != null && targets.isAnimatingHome();
+                if (!isAnimatingToRecents) {
+                    rv.setAlpha(1);
+                }
+                createActivityController(getSwipeUpDestinationAndLength(
+                        activity.getDeviceProfile(), activity, new Rect()));
+            }
+
+            @Override
+            public void createActivityController(long transitionLength) {
+                if (!isAnimatingToRecents) {
+                    return;
+                }
+
+                ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
+                anim.setDuration(transitionLength).setInterpolator(LINEAR);
+                AnimatorSet animatorSet = new AnimatorSet();
+                animatorSet.play(anim);
+                callback.accept(AnimatorPlaybackController.wrap(animatorSet, transitionLength));
+            }
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<RecentsActivity, Boolean> onInitListener) {
+        return new RecentsActivityTracker(onInitListener);
+    }
+
+    @Nullable
+    @Override
+    public RecentsActivity getCreatedActivity() {
+        return RecentsActivityTracker.getCurrentActivity();
+    }
+
+    @Nullable
+    @Override
+    public IconRecentsView getVisibleRecentsView() {
+        RecentsActivity activity = getCreatedActivity();
+        if (activity != null && activity.hasWindowFocus()) {
+            return activity.getOverviewPanel();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        return false;
+    }
+
+    @Override
+    public AlphaProperty getAlphaProperty(RecentsActivity activity) {
+        return activity.getDragLayer().getAlphaProperty(0);
+    }
+
+    @Override
+    public int getContainerType() {
+        return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
new file mode 100644
index 0000000..7078871
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
@@ -0,0 +1,59 @@
+package com.android.quickstep;
+
+import android.content.Context;
+import android.graphics.Rect;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Base activity control helper for Go that stubs out most of the functionality that is not needed
+ * for Go.
+ *
+ * @param <T> activity that contains the overview
+ */
+public abstract class GoActivityControlHelper<T extends BaseDraggingActivity> implements
+        ActivityControlHelper<T> {
+
+    @Override
+    public void onTransitionCancelled(T activity, boolean activityVisible) {
+        // Go transitions to overview are all atomic.
+    }
+
+    @Override
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect 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(T activity) {
+        // Go does not support swipe up gesture.
+    }
+
+    @Override
+    public HomeAnimationFactory prepareHomeUI(T activity) {
+        // Go does not support gestures from app to home.
+        return null;
+    }
+
+    @Override
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+        // Go does not support gestures to overview.
+        return null;
+    }
+
+    @Override
+    public boolean shouldMinimizeSplitScreen() {
+        // Go does not support split screen.
+        return true;
+    }
+
+    @Override
+    public boolean isInLiveTileMode() {
+        // Go does not support live tiles.
+        return false;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index e1df3ba..76685f3 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -18,81 +18,23 @@
 
 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.
+ * {@link ActivityControlHelper} for the in-launcher recents.
  * 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;
-    }
+public final class LauncherActivityControllerHelper extends GoActivityControlHelper<Launcher> {
 
     @Override
     public AnimationFactory prepareRecentsUI(Launcher activity,
@@ -101,8 +43,7 @@
         //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) {}
+            public void createActivityController(long transitionLength) {}
 
             @Override
             public void onTransitionCancelled() {}
@@ -138,64 +79,29 @@
     }
 
     @Override
-    public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
         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);
+        launcher.getUserEventDispatcher().logActionCommand(
+                LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                getContainerType(),
+                LauncherLogProto.ContainerType.TASKSWITCHER);
+        launcher.getStateManager().goToState(OVERVIEW,
+                launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
         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/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
similarity index 86%
rename from quickstep/src/com/android/quickstep/OverviewCommandHelper.java
rename to go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 5b4f673..d9873fe 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -30,7 +30,7 @@
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
-import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.IconRecentsView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -67,10 +67,14 @@
         mMainThreadExecutor.execute(new RecentsActivityCommand<>());
     }
 
-    public void onOverviewShown() {
+    public void onOverviewShown(boolean triggeredFromAltTab) {
         mMainThreadExecutor.execute(new ShowRecentsCommand());
     }
 
+    public void onOverviewHidden() {
+        mMainThreadExecutor.execute(new HideRecentsCommand());
+    }
+
     public void onTip(int actionType, int viewType) {
         mMainThreadExecutor.execute(() ->
                 UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
@@ -84,6 +88,19 @@
         }
     }
 
+    private class HideRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            IconRecentsView recents = (IconRecentsView) mHelper.getVisibleRecentsView();
+            if (recents == null) {
+                return false;
+            }
+            //TODO: Launch last running task or go to home.
+            return true;
+        }
+    }
+
     private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
 
         protected final ActivityControlHelper<T> mHelper;
@@ -114,7 +131,7 @@
                 return;
             }
 
-            if (mHelper.switchToRecentsIfVisible(true)) {
+            if (mHelper.switchToRecentsIfVisible(null /* onCompleteCallback */)) {
                 // If successfully switched, then return
                 return;
             }
@@ -127,13 +144,9 @@
         }
 
         protected boolean handleCommand(long elapsedTime) {
-            // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
-            //       the menu activity which takes window focus, preventing the right condition from
-            //       being run below
-            RecentsView recents = mHelper.getVisibleRecentsView();
+            IconRecentsView recents = mHelper.getVisibleRecentsView();
             if (recents != null) {
-                // Launch the next task
-                recents.showNextTask();
+                //TODO: Launch next task in icon recents.
                 return true;
             } else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
                 // The user tried to launch back into overview too quickly, either after
diff --git a/go/quickstep/src/com/android/quickstep/RecentsActivity.java b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
new file mode 100644
index 0000000..a186aaa
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.app.ActivityOptions;
+import android.view.View;
+
+import com.android.launcher3.R;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.fallback.GoRecentsActivityRootView;
+import com.android.quickstep.views.IconRecentsView;
+
+/**
+ * A recents activity that displays recent tasks with an icon and small snapshot.
+ */
+public final class RecentsActivity extends BaseRecentsActivity {
+
+    private GoRecentsActivityRootView mRecentsRootView;
+    private IconRecentsView mIconRecentsView;
+
+    @Override
+    protected void initViews() {
+        setContentView(R.layout.fallback_recents_activity);
+        mRecentsRootView = findViewById(R.id.drag_layer);
+        mIconRecentsView = findViewById(R.id.overview_panel);
+    }
+
+    @Override
+    protected void reapplyUi() {
+        //TODO: Implement this depending on how insets will affect the view.
+    }
+
+    @Override
+    public BaseDragLayer getDragLayer() {
+        return mRecentsRootView;
+    }
+
+    @Override
+    public View getRootView() {
+        return mRecentsRootView;
+    }
+
+    @Override
+    public <T extends View> T getOverviewPanel() {
+        return (T) mIconRecentsView;
+    }
+
+    @Override
+    public ActivityOptions getActivityLaunchOptions(View v) {
+        //TODO: Hook into recents launch animation
+        return null;
+    }
+
+    @Override
+    protected void onStart() {
+        // Set the alpha to 1 before calling super, as it may get set back to 0 due to
+        // onActivityStart callback.
+        mIconRecentsView.setAlpha(0);
+        super.onStart();
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
new file mode 100644
index 0000000..77c3f33
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView.Adapter;
+
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
+/**
+ * Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
+ * appropriate {@link Task} from the recents task list.
+ */
+public final class TaskAdapter extends Adapter<TaskHolder> {
+
+    private static final int MAX_TASKS_TO_DISPLAY = 6;
+    private static final String TAG = "TaskAdapter";
+    private final ArrayList<Task> mTaskList;
+
+    public TaskAdapter(@NonNull ArrayList<Task> taskList) {
+        mTaskList = taskList;
+    }
+
+    @Override
+    public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+        // TODO: Swap in an actual task view here (view w/ icon, label, etc.)
+        TextView stubView = new TextView(parent.getContext());
+        return new TaskHolder(stubView);
+    }
+
+    @Override
+    public void onBindViewHolder(TaskHolder holder, int position) {
+        holder.bindTask(mTaskList.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return Math.min(mTaskList.size(), MAX_TASKS_TO_DISPLAY);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
new file mode 100644
index 0000000..1ea6d76
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -0,0 +1,48 @@
+/*
+ * 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 android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView.ViewHolder;
+
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * A recycler view holder that holds the task view and binds {@link Task} content (app title, icon,
+ * etc.) to the view.
+ */
+final class TaskHolder extends ViewHolder {
+
+    // TODO: Implement the actual task view to be held.
+    // For now, we just use a simple text view.
+    private final TextView mStubView;
+
+    public TaskHolder(TextView stubView) {
+        super(stubView);
+        mStubView = stubView;
+    }
+
+    /**
+     * Bind task content to the view. This includes the task icon and title as well as binding
+     * input handlers such as which task to launch/remove.
+     *
+     * @param task the task to bind to the view this
+     */
+    public void bindTask(Task task) {
+        mStubView.setText("Stub task view: " + task.titleDescription);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/TouchInteractionService.java b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
new file mode 100644
index 0000000..2858deb
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -0,0 +1,133 @@
+/*
+ * 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.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+
+import android.annotation.TargetApi;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Region;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
+
+/**
+ * Service connected by system-UI for handling touch interaction.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class TouchInteractionService extends Service {
+
+    public static final int EDGE_NAV_BAR = 1 << 8;
+
+    private static final String TAG = "TouchInteractionService";
+
+    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+
+        @Override
+        public void onActiveNavBarRegionChanges(Region region) throws RemoteException { }
+
+        @Override
+        public void onInitialize(Bundle bundle) throws RemoteException {
+            ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
+                    .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+            mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+        }
+
+        @Override
+        public void onOverviewToggle() {
+            mOverviewCommandHelper.onOverviewToggle();
+        }
+
+        @Override
+        public void onOverviewShown(boolean triggeredFromAltTab) {
+            mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+        }
+
+        @Override
+        public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+            if (triggeredFromAltTab && !triggeredFromHomeKey) {
+                // onOverviewShownFromAltTab hides the overview and ends at the target app
+                mOverviewCommandHelper.onOverviewHidden();
+            }
+        }
+
+        @Override
+        public void onTip(int actionType, int viewType) {
+            mOverviewCommandHelper.onTip(actionType, viewType);
+        }
+
+        /** Deprecated methods **/
+        public void onQuickStep(MotionEvent motionEvent) { }
+
+        public void onQuickScrubEnd() { }
+
+        public void onQuickScrubProgress(float progress) { }
+
+        public void onQuickScrubStart() { }
+
+        public void onPreMotionEvent(int downHitTarget) { }
+
+        public void onMotionEvent(MotionEvent ev) { }
+
+        public void onBind(ISystemUiProxy iSystemUiProxy) {
+            mRecentsModel.setSystemUiProxy(iSystemUiProxy);
+        }
+    };
+
+    private static boolean sConnected = false;;
+
+    public static boolean isConnected() {
+        return sConnected;
+    }
+
+    private RecentsModel mRecentsModel;
+    private OverviewComponentObserver mOverviewComponentObserver;
+    private OverviewCommandHelper mOverviewCommandHelper;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mRecentsModel = RecentsModel.INSTANCE.get(this);
+        mOverviewComponentObserver = new OverviewComponentObserver(this);
+        mOverviewCommandHelper = new OverviewCommandHelper(this,
+                mOverviewComponentObserver);
+
+        sConnected = true;
+    }
+
+    @Override
+    public void onDestroy() {
+        mOverviewComponentObserver.onDestroy();
+        sConnected = false;
+        super.onDestroy();
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "Touch service connected");
+        }
+        return mMyBinder;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
new file mode 100644
index 0000000..d748e89
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/fallback/GoRecentsActivityRootView.java
@@ -0,0 +1,31 @@
+/*
+ * 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.fallback;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.RecentsActivity;
+
+/**
+ * Minimal implementation of {@link BaseDragLayer} for Go's fallback recents activity.
+ */
+public final class GoRecentsActivityRootView extends BaseDragLayer<RecentsActivity> {
+    public GoRecentsActivityRootView(Context context, AttributeSet attrs) {
+        super(context, attrs, 1 /* alphaChannelCount */);
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e4741e9..15da10c 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -15,14 +15,27 @@
  */
 package com.android.quickstep.views;
 
+import static androidx.recyclerview.widget.LinearLayoutManager.VERTICAL;
+
 import android.content.Context;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.view.ViewDebug;
 import android.widget.FrameLayout;
 
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.launcher3.R;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.TaskAdapter;
+import com.android.systemui.shared.recents.model.Task;
+
+import java.util.ArrayList;
+
 /**
- * Root view for the icon recents view.
+ * Root view for the icon recents view. Acts as the main interface to the rest of the Launcher code
+ * base.
  */
 public final class IconRecentsView extends FrameLayout {
 
@@ -45,6 +58,11 @@
                 @Override
                 public void setValue(IconRecentsView view, float v) {
                     ALPHA.set(view, v);
+                    if (view.getVisibility() != VISIBLE && v > 0) {
+                        view.setVisibility(VISIBLE);
+                    } else if (view.getVisibility() != GONE && v == 0){
+                        view.setVisibility(GONE);
+                    }
                 }
 
                 @Override
@@ -58,10 +76,28 @@
      * is top aligned and 0.5 is centered vertically.
      */
     @ViewDebug.ExportedProperty(category = "launcher")
+
+    // TODO: Write a recents task list observer that creates/updates tasks and signals task adapter.
+    private static final ArrayList<Task> DUMMY_TASK_LIST = new ArrayList<>();
+    private final Context mContext;
+
     private float mTranslationYFactor;
+    private TaskAdapter mTaskAdapter;
+    private RecyclerView mTaskRecyclerView;
 
     public IconRecentsView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mContext = context;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mTaskAdapter = new TaskAdapter(DUMMY_TASK_LIST);
+        mTaskRecyclerView = findViewById(R.id.recent_task_recycler_view);
+        mTaskRecyclerView.setAdapter(mTaskAdapter);
+        mTaskRecyclerView.setLayoutManager(
+                new LinearLayoutManager(mContext, VERTICAL, true /* reverseLayout */));
     }
 
     public void setTranslationYFactor(float translationFactor) {
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 9ccd477..8c58e3e 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml b/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
new file mode 100644
index 0000000..7b4da83
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/drawable/chip_hint_background_light.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+    <stroke
+        android:color="@color/chip_hint_foreground_color"
+        android:width="@dimen/chip_hint_border_width"/>
+    <corners android:radius="@dimen/chip_hint_corner_radius"/>
+    <padding
+        android:left="@dimen/chip_hint_outer_padding"
+        android:top="@dimen/chip_hint_outer_padding"
+        android:right="@dimen/chip_hint_outer_padding"
+        android:bottom="@dimen/chip_hint_outer_padding"/>
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/layout/fallback_recents_activity.xml b/quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
similarity index 100%
rename from quickstep/res/layout/fallback_recents_activity.xml
rename to quickstep/recents_ui_overrides/res/layout/fallback_recents_activity.xml
diff --git a/quickstep/recents_ui_overrides/res/layout/hint.xml b/quickstep/recents_ui_overrides/res/layout/hint.xml
new file mode 100644
index 0000000..7e2d6af
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/hint.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<com.android.quickstep.hints.HintView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="@dimen/chip_hint_height"
+    android:layout_gravity="center_horizontal|bottom"
+    android:paddingStart="@dimen/chip_hint_start_padding"
+    android:paddingEnd="@dimen/chip_hint_end_padding"
+    android:background="@drawable/chip_hint_background_light"
+    android:gravity="center"
+    android:layout_marginHorizontal="@dimen/chip_hint_horizontal_margin"
+    android:orientation="horizontal"
+    android:elevation="@dimen/chip_hint_elevation"
+    android:layoutDirection="ltr">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="@dimen/chip_icon_size"
+        android:layout_height="@dimen/chip_icon_size"
+        android:visibility="gone"
+        android:scaleType="fitCenter"
+        android:adjustViewBounds="true"
+        android:contentDescription="@null"/>
+
+    <TextView
+        android:id="@+id/label"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/chip_text_height"
+        android:paddingTop="@dimen/chip_text_top_padding"
+        android:paddingStart="@dimen/chip_text_start_padding"
+        android:fontFamily="google-sans-medium"
+        android:textAlignment="textStart"
+        android:singleLine="true"
+        android:textColor="@color/chip_hint_foreground_color"
+        android:textSize="@dimen/chip_text_size"
+        android:ellipsize="none"
+        android:includeFontPadding="true"/>
+
+
+</com.android.quickstep.hints.HintView>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/layout/hint_container.xml b/quickstep/recents_ui_overrides/res/layout/hint_container.xml
new file mode 100644
index 0000000..336f63e
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/layout/hint_container.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<com.android.quickstep.hints.HintsContainer
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center_horizontal"
+    android:orientation="horizontal"/>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/colors.xml b/quickstep/recents_ui_overrides/res/values/colors.xml
new file mode 100644
index 0000000..1e8d0cc
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/colors.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="chip_hint_foreground_color">#fff</color>
+</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/res/values/dimens.xml b/quickstep/recents_ui_overrides/res/values/dimens.xml
new file mode 100644
index 0000000..b654d5c
--- /dev/null
+++ b/quickstep/recents_ui_overrides/res/values/dimens.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <dimen name="chip_hint_border_width">1dp</dimen>
+    <dimen name="chip_hint_corner_radius">20dp</dimen>
+    <dimen name="chip_hint_outer_padding">20dp</dimen>
+    <dimen name="chip_hint_start_padding">10dp</dimen>
+    <dimen name="chip_hint_end_padding">12dp</dimen>
+    <dimen name="chip_hint_horizontal_margin">20dp</dimen>
+    <dimen name="chip_hint_elevation">2dp</dimen>
+    <dimen name="chip_icon_size">16dp</dimen>
+    <dimen name="chip_text_height">26dp</dimen>
+    <dimen name="chip_text_top_padding">4dp</dimen>
+    <dimen name="chip_text_start_padding">10dp</dimen>
+    <dimen name="chip_text_size">14sp</dimen>
+</resources>
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 9921455..1042c60 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -19,8 +19,8 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
+import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -61,33 +61,16 @@
     }
 
     @Override
-    protected boolean isQuickSwitchInProgress() {
-        return mRecentsView.getQuickScrubController().isQuickSwitch();
-    }
-
-    @Override
-    protected ActivityOptions getQuickSwitchActivityOptions() {
-        return ActivityOptions.makeCustomAnimation(mLauncher, R.anim.no_anim,
-                R.anim.no_anim);
-    }
-
-    @Override
     protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
             @NonNull RemoteAnimationTargetCompat[] targets, boolean launcherClosing) {
         RecentsView recentsView = mLauncher.getOverviewPanel();
         boolean skipLauncherChanges = !launcherClosing;
-        boolean isLaunchingFromQuickscrub =
-                recentsView.getQuickScrubController().isWaitingForTaskLaunch();
 
         TaskView taskView = findTaskViewToLaunch(mLauncher, v, targets);
 
-        int duration = isLaunchingFromQuickscrub
-                ? RECENTS_QUICKSCRUB_LAUNCH_DURATION
-                : RECENTS_LAUNCH_DURATION;
-
         ClipAnimationHelper helper = new ClipAnimationHelper(mLauncher);
         anim.play(getRecentsWindowAnimator(taskView, skipLauncherChanges, targets, helper)
-                .setDuration(duration));
+                .setDuration(RECENTS_LAUNCH_DURATION));
 
         Animator childStateAnimation = null;
         // Found a visible recents task that matches the opening app, lets launch the app from there
@@ -96,7 +79,7 @@
         if (launcherClosing) {
             launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView, helper);
             launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
-            launcherAnim.setDuration(duration);
+            launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
 
             // Make sure recents gets fixed up by resetting task alphas and scales, etc.
             windowAnimEndListener = new AnimatorListenerAdapter() {
@@ -108,10 +91,11 @@
             };
         } else {
             AnimatorPlaybackController controller =
-                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL, duration);
+                    mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
+                            RECENTS_LAUNCH_DURATION);
             controller.dispatchOnStart();
             childStateAnimation = controller.getTarget();
-            launcherAnim = controller.getAnimationPlayer().setDuration(duration);
+            launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
             windowAnimEndListener = new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
index 8d28f33..963f1fa 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -15,10 +15,11 @@
  */
 package com.android.launcher3.uioverrides;
 
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+
 import android.os.RemoteException;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.util.LayoutUtils;
 import com.android.quickstep.views.RecentsView;
@@ -33,7 +34,7 @@
             FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
 
     public BackgroundAppState(int id) {
-        super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
+        super(id, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
     }
 
     @Override
@@ -51,7 +52,7 @@
 
     @Override
     public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        // Initialize the recents view scale to what it would be when starting swipe up/quickscrub
+        // Initialize the recents view scale to what it would be when starting swipe up
         RecentsView recentsView = launcher.getOverviewPanel();
         recentsView.getTaskSize(sTempRect);
         int appWidth = launcher.getDragLayer().getWidth();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FastOverviewState.java
deleted file mode 100644
index 1d65a54..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.launcher3.uioverrides;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Rect;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.R;
-import com.android.quickstep.QuickScrubController;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Extension of overview state used for QuickScrub
- */
-public class FastOverviewState extends OverviewState {
-
-    private static final float MAX_PREVIEW_SCALE_UP = 1.3f;
-    /**
-     * Vertical transition of the task previews relative to the full container.
-     */
-    public static final float OVERVIEW_TRANSLATION_FACTOR = 0.4f;
-    public static final float OVERVIEW_CENTERED_TRANSLATION_FACTOR = 0.5f;
-
-    private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_DISABLE_INTERACTION
-            | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
-
-    public FastOverviewState(int id) {
-        super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
-    }
-
-    @Override
-    public void onStateTransitionEnd(Launcher launcher) {
-        super.onStateTransitionEnd(launcher);
-        RecentsView recentsView = launcher.getOverviewPanel();
-        recentsView.getQuickScrubController().onFinishedTransitionToQuickScrub();
-    }
-
-    @Override
-    public int getVisibleElements(Launcher launcher) {
-        return NONE;
-    }
-
-    @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        RecentsView recentsView = launcher.getOverviewPanel();
-        recentsView.getTaskSize(sTempRect);
-
-        boolean isQuickSwitch = recentsView.getQuickScrubController().isQuickSwitch();
-        float translationYFactor = isQuickSwitch
-                ? OVERVIEW_CENTERED_TRANSLATION_FACTOR
-                : OVERVIEW_TRANSLATION_FACTOR;
-        return new float[] {getOverviewScale(launcher.getDeviceProfile(), sTempRect, launcher,
-                isQuickSwitch), translationYFactor};
-    }
-
-    public static float getOverviewScale(DeviceProfile dp, Rect taskRect, Context context,
-            boolean isQuickSwitch) {
-        if (dp.isVerticalBarLayout() && !isQuickSwitch) {
-            return 1f;
-        }
-
-        Resources res = context.getResources();
-        float usedHeight = taskRect.height() + res.getDimension(R.dimen.task_thumbnail_top_margin);
-        float usedWidth = taskRect.width() + 2 * (res.getDimension(R.dimen.recents_page_spacing)
-                + res.getDimension(R.dimen.quickscrub_adjacent_visible_width));
-        if (isQuickSwitch) {
-            usedWidth = taskRect.width();
-            return Math.max(dp.availableHeightPx / usedHeight, dp.availableWidthPx / usedWidth);
-        }
-        return Math.min(Math.min(dp.availableHeightPx / usedHeight,
-                dp.availableWidthPx / usedWidth), MAX_PREVIEW_SCALE_UP);
-    }
-
-    @Override
-    public void onStateDisabled(Launcher launcher) {
-        super.onStateDisabled(launcher);
-        launcher.<RecentsView>getOverviewPanel().getQuickScrubController().cancelActiveQuickscrub();
-    }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
index b37c2e0..a41362f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
@@ -91,5 +91,6 @@
         } else {
             super.onDragEnd(velocity, fling);
         }
+        mMotionPauseDetector.clear();
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
index de6f7a7..3602508 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -76,18 +75,13 @@
     public void onStateEnabled(Launcher launcher) {
         RecentsView rv = launcher.getOverviewPanel();
         rv.setOverviewStateEnabled(true);
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            AbstractFloatingView.closeAllOpenViews(launcher);
-        } else {
-            AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
-        }
+        AbstractFloatingView.closeAllOpenViews(launcher);
     }
 
     @Override
     public void onStateDisabled(Launcher launcher) {
         RecentsView rv = launcher.getOverviewPanel();
         rv.setOverviewStateEnabled(false);
-        RecentsModel.INSTANCE.get(launcher).resetAssistCache();
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 51e9495..027fd91 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -120,13 +120,11 @@
      */
     public static void onLauncherStateOrResumeChanged(Launcher launcher) {
         LauncherState state = launcher.getStateManager().getState();
-        if (!OverviewInteractionState.INSTANCE.get(launcher).swipeGestureInitializing()) {
-            DeviceProfile profile = launcher.getDeviceProfile();
-            boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
-                    && !profile.isVerticalBarLayout();
-            UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
-                    visible ? 1 : 0, profile.hotseatBarSizePx);
-        }
+        DeviceProfile profile = launcher.getDeviceProfile();
+        boolean visible = (state == NORMAL || state == OVERVIEW) && launcher.isUserActive()
+                && !profile.isVerticalBarLayout();
+        UiThreadHelper.runAsyncCommand(launcher, SET_SHELF_HEIGHT_CMD,
+                visible ? 1 : 0, profile.hotseatBarSizePx);
 
         if (state == NORMAL) {
             launcher.<RecentsView>getOverviewPanel().setSwipeDownShouldLaunchApp(false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 7d7946d..0b3bd6c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,10 +15,6 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
@@ -26,7 +22,6 @@
 import android.annotation.TargetApi;
 import android.os.Build;
 import android.util.FloatProperty;
-import android.view.animation.Interpolator;
 
 import androidx.annotation.NonNull;
 
@@ -34,7 +29,6 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 
@@ -56,6 +50,9 @@
         if (state.overviewUi) {
             mRecentsView.updateEmptyMessage();
             mRecentsView.resetTaskVisuals();
+            mRecentsView.setHintVisibility(1);
+        } else {
+            mRecentsView.setHintVisibility(0);
         }
     }
 
@@ -66,6 +63,7 @@
 
         if (!toState.overviewUi) {
             builder.addOnFinishRunnable(mRecentsView::resetTaskVisuals);
+            mRecentsView.setHintVisibility(0);
         }
 
         if (toState.overviewUi) {
@@ -77,20 +75,11 @@
             updateAnim.setDuration(config.duration);
             builder.play(updateAnim);
             mRecentsView.updateEmptyMessage();
+            builder.addOnFinishRunnable(() -> mRecentsView.setHintVisibility(1));
         }
     }
 
     @Override
-    Interpolator getScaleAndTransYInterpolator(@NonNull LauncherState toState,
-            @NonNull AnimatorSetBuilder builder) {
-        if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) {
-            return Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
-                    QUICK_SCRUB_TRANSLATION_Y_FACTOR);
-        }
-        return super.getScaleAndTransYInterpolator(toState, builder);
-    }
-
-    @Override
     FloatProperty<LauncherRecentsView> getTranslationYFactorProperty() {
         return TRANSLATION_Y_FACTOR;
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
similarity index 100%
rename from quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
diff --git a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
similarity index 96%
rename from quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index e0eeda5..e5747dc 100644
--- a/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -17,7 +17,6 @@
 
 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;
 
@@ -34,7 +33,6 @@
 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;
@@ -82,7 +80,7 @@
                     anim.start();
                 });
         factory.onRemoteAnimationReceived(null);
-        factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL);
+        factory.createActivityController(RECENTS_LAUNCH_DURATION);
         mActivity = activity;
         mRecentsView = mActivity.getOverviewPanel();
         return false;
@@ -136,9 +134,8 @@
                 loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
         clipHelper.updateSource(homeBounds, runningTaskTarget);
 
-        TransformedRect targetRect = new TransformedRect();
-        mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
-                INTERACTION_NORMAL, targetRect);
+        Rect targetRect = new Rect();
+        mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity, targetRect);
         clipHelper.updateTargetRect(targetRect);
         clipHelper.prepareAnimation(false /* isOpening */);
 
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
similarity index 66%
rename from quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index aba6060..1ed1353 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -15,41 +15,33 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.Animator;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.os.Handler;
-import android.os.Looper;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.TouchConsumer.InteractionType;
 import com.android.quickstep.util.LayoutUtils;
 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 java.util.Objects;
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 /**
  * {@link ActivityControlHelper} for recents when the default launcher is different than the
  * currently running one and apps should interact with the {@link RecentsActivity} as opposed
@@ -58,39 +50,7 @@
 public final class FallbackActivityControllerHelper implements
         ActivityControlHelper<RecentsActivity> {
 
-    private final ComponentName mHomeComponent;
-    private final Handler mUiHandler = new Handler(Looper.getMainLooper());
-
-    public FallbackActivityControllerHelper(ComponentName homeComponent) {
-        mHomeComponent = homeComponent;
-    }
-
-    @Override
-    public void onQuickInteractionStart(RecentsActivity activity, RunningTaskInfo taskInfo,
-            boolean activityVisible, TouchInteractionLog touchInteractionLog) {
-        QuickScrubController controller = activity.<RecentsView>getOverviewPanel()
-                .getQuickScrubController();
-
-        // TODO: match user is as well
-        boolean startingFromHome = !activityVisible &&
-                (taskInfo == null || Objects.equals(taskInfo.topActivity, mHomeComponent));
-        controller.onQuickScrubStart(startingFromHome, this, touchInteractionLog);
-        if (activityVisible) {
-            mUiHandler.postDelayed(controller::onFinishedTransitionToQuickScrub,
-                    OVERVIEW_TRANSITION_MS);
-        }
-    }
-
-    @Override
-    public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
-            Context context) {
-        return 0;
-    }
-
-    @Override
-    public void executeOnWindowAvailable(RecentsActivity activity, Runnable action) {
-        action.run();
-    }
+    public FallbackActivityControllerHelper() { }
 
     @Override
     public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
@@ -98,15 +58,14 @@
     }
 
     @Override
-    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect) {
-        LayoutUtils.calculateFallbackTaskSize(context, dp, outRect.rect);
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+        LayoutUtils.calculateFallbackTaskSize(context, dp, outRect);
         if (dp.isVerticalBarLayout()) {
             Rect targetInsets = dp.getInsets();
             int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
             return dp.hotseatBarSizePx + hotseatInset;
         } else {
-            return dp.heightPx - outRect.rect.bottom;
+            return dp.heightPx - outRect.bottom;
         }
     }
 
@@ -148,7 +107,7 @@
     public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
             boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
         if (activityVisible) {
-            return (transitionLength, interactionType) -> { };
+            return (transitionLength) -> { };
         }
 
         RecentsView rv = activity.getOverviewPanel();
@@ -165,12 +124,11 @@
                     rv.setContentAlpha(1);
                 }
                 createActivityController(getSwipeUpDestinationAndLength(
-                        activity.getDeviceProfile(), activity, INTERACTION_NORMAL,
-                        new TransformedRect()), INTERACTION_NORMAL);
+                        activity.getDeviceProfile(), activity, new Rect()));
             }
 
             @Override
-            public void createActivityController(long transitionLength, int interactionType) {
+            public void createActivityController(long transitionLength) {
                 if (!isAnimatingToRecents) {
                     return;
                 }
@@ -185,26 +143,6 @@
     }
 
     @Override
-    public LayoutListener createLayoutListener(RecentsActivity activity) {
-        // We do not change anything as part of layout changes in fallback activity. Return a
-        // default layout listener.
-        return new LayoutListener() {
-            @Override
-            public void open() { }
-
-            @Override
-            public void setHandler(WindowTransformSwipeHandler handler) { }
-
-            @Override
-            public void finish() { }
-
-            @Override
-            public void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect,
-                    float cornerRadius) { }
-        };
-    }
-
-    @Override
     public ActivityInitListener createActivityInitListener(
             BiPredicate<RecentsActivity, Boolean> onInitListener) {
         return new RecentsActivityTracker(onInitListener);
@@ -227,17 +165,11 @@
     }
 
     @Override
-    public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
         return false;
     }
 
     @Override
-    public boolean deferStartingActivity(int downHitTarget) {
-        // Always defer starting the activity when using fallback
-        return true;
-    }
-
-    @Override
     public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
         // TODO: Remove this once b/77875376 is fixed
         return target.sourceContainerBounds;
@@ -250,16 +182,6 @@
     }
 
     @Override
-    public boolean supportsLongSwipe(RecentsActivity activity) {
-        return false;
-    }
-
-    @Override
-    public LongSwipeHelper getLongSwipeController(RecentsActivity activity, int runningTaskId) {
-        return null;
-    }
-
-    @Override
     public AlphaProperty getAlphaProperty(RecentsActivity activity) {
         return activity.getDragLayer().getAlphaProperty(0);
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
new file mode 100644
index 0000000..8dfb9ab
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/InputConsumer.java
@@ -0,0 +1,48 @@
+/*
+ * 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;
+
+import android.annotation.TargetApi;
+import android.os.Build;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+@TargetApi(Build.VERSION_CODES.O)
+public interface InputConsumer {
+    InputConsumer NO_OP = new InputConsumer() { };
+
+    default boolean isActive() {
+        return false;
+    }
+
+    /**
+     * Called by the event queue when the consumer is about to be switched to a new consumer.
+     */
+    default void onConsumerAboutToBeSwitched() { }
+
+    default void onMotionEvent(MotionEvent ev) { }
+
+    default void onKeyEvent(KeyEvent ev) { }
+
+    default void onInputEvent(InputEvent ev) {
+        if (ev instanceof MotionEvent) {
+            onMotionEvent((MotionEvent) ev);
+        } else {
+            onKeyEvent((KeyEvent) ev);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
index af25355..ffd3b4b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -19,54 +19,41 @@
 
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.FAST_OVERVIEW;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO;
 import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.app.ActivityManager.RunningTaskInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.Region;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
 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.R;
-import com.android.launcher3.TestProtocol;
 import com.android.launcher3.allapps.DiscoveryBounce;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.SpringObjectAnimator;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.uioverrides.FastOverviewState;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.TouchConsumer.InteractionType;
+import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
-import com.android.quickstep.util.TransformedRect;
-import com.android.quickstep.views.LauncherLayoutListener;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -74,70 +61,18 @@
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
 /**
  * {@link ActivityControlHelper} for the in-launcher recents.
  */
 public final class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher> {
 
     @Override
-    public LayoutListener createLayoutListener(Launcher activity) {
-        return LauncherLayoutListener.resetAndGet(activity);
-    }
-
-    @Override
-    public void onQuickInteractionStart(Launcher activity, RunningTaskInfo taskInfo,
-            boolean activityVisible, TouchInteractionLog touchInteractionLog) {
-        LauncherState fromState = activity.getStateManager().getState();
-        QuickScrubController controller = activity.<RecentsView>getOverviewPanel()
-                .getQuickScrubController();
-        boolean isQuickSwitch = controller.isQuickSwitch();
-        boolean animate = activityVisible;
-        if (isQuickSwitch && fromState == FAST_OVERVIEW && !animate) {
-            // We can already be in FAST_OVERVIEW if createActivityController() was called
-            // before us. This could happen, for instance, when launcher is slow to load when
-            // starting quick switch, causing us to call onQuickScrubStart() on the background
-            // thread. In this case, we also hadn't set isQuickSwitch = true before setting
-            // FAST_OVERVIEW, so we need to reapply FAST_OVERVIEW to take that into account.
-            activity.getStateManager().reapplyState();
-        } else {
-            activity.getStateManager().goToState(FAST_OVERVIEW, animate);
-        }
-
-        controller.onQuickScrubStart(activityVisible && !fromState.overviewUi, this,
-                touchInteractionLog);
-
-        if (!activityVisible) {
-            // For the duration of the gesture, lock the screen orientation to ensure that we
-            // do not rotate mid-quickscrub
-            activity.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
-        }
-    }
-
-    @Override
-    public float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
-            Context context) {
-        // The padding calculations are exactly same as that of RecentsView.setInsets
-        int topMargin = context.getResources()
-                .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
-        int paddingTop = targetRect.rect.top - topMargin - dp.getInsets().top;
-        int paddingBottom = dp.heightPx - dp.getInsets().bottom - targetRect.rect.bottom;
-
-        return FastOverviewState.OVERVIEW_TRANSLATION_FACTOR * (paddingBottom - paddingTop);
-    }
-
-    @Override
-    public void executeOnWindowAvailable(Launcher activity, Runnable action) {
-        activity.getWorkspace().runOnOverlayHidden(action);
-    }
-
-    @Override
-    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect) {
-        LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect);
-        if (interactionType == INTERACTION_QUICK_SCRUB) {
-            outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context,
-                    FeatureFlags.QUICK_SWITCH.get());
-        }
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+        LayoutUtils.calculateLauncherTaskSize(context, dp, outRect);
         if (dp.isVerticalBarLayout()) {
             Rect targetInsets = dp.getInsets();
             int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
@@ -163,17 +98,41 @@
     @NonNull
     @Override
     public HomeAnimationFactory prepareHomeUI(Launcher activity) {
-        DeviceProfile dp = activity.getDeviceProfile();
+        final DeviceProfile dp = activity.getDeviceProfile();
+        final RecentsView recentsView = activity.getOverviewPanel();
+        final ComponentName component = recentsView.getRunningTaskView().getTask().key
+                .sourceComponent;
+
+        final View workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
+        final FloatingIconView floatingView = workspaceView == null ? null
+                : new FloatingIconView(activity);
+        final Rect iconLocation = new Rect();
+        if (floatingView != null) {
+            floatingView.matchPositionOf(activity, workspaceView, true /* hideOriginal */,
+                    iconLocation);
+        }
 
         return new HomeAnimationFactory() {
+            @Nullable
+            @Override
+            public View getFloatingView() {
+                return floatingView;
+            }
+
             @NonNull
             @Override
             public RectF getWindowTargetRect() {
-                int halfIconSize = dp.iconSizePx / 2;
-                float targetCenterX = dp.availableWidthPx / 2;
-                float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
-                return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
-                        targetCenterX + halfIconSize, targetCenterY + halfIconSize);
+                final int halfIconSize = dp.iconSizePx / 2;
+                final float targetCenterX = dp.availableWidthPx / 2f;
+                final float targetCenterY = dp.availableHeightPx - dp.hotseatBarSizePx;
+
+                if (workspaceView != null) {
+                    return new RectF(iconLocation);
+                } else {
+                    // Fallback to animate to center of screen.
+                    return new RectF(targetCenterX - halfIconSize, targetCenterY - halfIconSize,
+                            targetCenterX + halfIconSize, targetCenterY + halfIconSize);
+                }
             }
 
             @NonNull
@@ -208,8 +167,7 @@
             // Optimization, hide the all apps view to prevent layout while initializing
             activity.getAppsView().getContentView().setVisibility(View.GONE);
 
-            AccessibilityManagerCompat.sendEventToTest(
-                    activity, TestProtocol.SWITCHED_TO_STATE_MESSAGE);
+            AccessibilityManagerCompat.sendStateEventToTest(activity, fromState.ordinal);
         } else {
             fromState = startState;
         }
@@ -219,10 +177,9 @@
             private ShelfAnimState mShelfState;
 
             @Override
-            public void createActivityController(long transitionLength,
-                    @InteractionType int interactionType) {
+            public void createActivityController(long transitionLength) {
                 createActivityControllerInternal(activity, activityVisible, fromState,
-                        transitionLength, interactionType, callback);
+                        transitionLength, callback);
             }
 
             @Override
@@ -268,10 +225,8 @@
 
     private void createActivityControllerInternal(Launcher activity, boolean wasVisible,
             LauncherState fromState, long transitionLength,
-            @InteractionType int interactionType,
             Consumer<AnimatorPlaybackController> callback) {
-        LauncherState endState = interactionType == INTERACTION_QUICK_SCRUB
-                ? FAST_OVERVIEW : OVERVIEW;
+        LauncherState endState = OVERVIEW;
         if (wasVisible && fromState != BACKGROUND_APP) {
             // If a translucent app was launched fom launcher, animate launcher states.
             DeviceProfile dp = activity.getDeviceProfile();
@@ -293,10 +248,7 @@
                     endState.getVerticalProgress(activity));
             anim.play(shiftAnim);
         }
-
-        if (interactionType == INTERACTION_NORMAL) {
-            playScaleDownAnim(anim, activity, endState);
-        }
+        playScaleDownAnim(anim, activity, endState);
 
         anim.setDuration(transitionLength * 2);
         activity.getStateManager().setCurrentAnimation(anim);
@@ -390,24 +342,24 @@
     }
 
     @Override
-    public boolean switchToRecentsIfVisible(boolean fromRecentsButton) {
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
         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);
+
+        launcher.getUserEventDispatcher().logActionCommand(
+                LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                getContainerType(),
+                LauncherLogProto.ContainerType.TASKSWITCHER);
+        launcher.getStateManager().goToState(OVERVIEW,
+                launcher.getStateManager().shouldAnimateStateChange(), onCompleteCallback);
         return true;
     }
 
     @Override
-    public boolean deferStartingActivity(int downHitTarget) {
-        return downHitTarget == HIT_TARGET_BACK || downHitTarget == HIT_TARGET_ROTATION;
+    public boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
+        return activeNavBarRegion.contains((int) ev.getX(), (int) ev.getY());
     }
 
     @Override
@@ -421,19 +373,6 @@
     }
 
     @Override
-    public boolean supportsLongSwipe(Launcher activity) {
-        return !activity.getDeviceProfile().isVerticalBarLayout();
-    }
-
-    @Override
-    public LongSwipeHelper getLongSwipeController(Launcher activity, int runningTaskId) {
-        if (activity.getDeviceProfile().isVerticalBarLayout()) {
-            return null;
-        }
-        return new LongSwipeHelper(activity, runningTaskId);
-    }
-
-    @Override
     public AlphaProperty getAlphaProperty(Launcher activity) {
         return activity.getDragLayer().getAlphaProperty(DragLayer.ALPHA_INDEX_SWIPE_UP);
     }
diff --git a/quickstep/src/com/android/quickstep/MultiStateCallback.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
similarity index 78%
rename from quickstep/src/com/android/quickstep/MultiStateCallback.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
index a408f95..9fceab4 100644
--- a/quickstep/src/com/android/quickstep/MultiStateCallback.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/MultiStateCallback.java
@@ -15,8 +15,6 @@
  */
 package com.android.quickstep;
 
-import static com.android.quickstep.WindowTransformSwipeHandler.STATES;
-
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -29,17 +27,28 @@
 public class MultiStateCallback {
 
     private static final String TAG = "MultiStateCallback";
-    private static final boolean DEBUG_STATES = false;
+    public static final boolean DEBUG_STATES = false;
 
     private final SparseArray<Runnable> mCallbacks = new SparseArray<>();
     private final SparseArray<Consumer<Boolean>> mStateChangeHandlers = new SparseArray<>();
 
+    private final String[] mStateNames;
+
+    public MultiStateCallback(String[] stateNames) {
+        mStateNames = DEBUG_STATES ? stateNames : null;
+    }
+
     private int mState = 0;
 
     /**
      * Adds the provided state flags to the global state and executes any callbacks as a result.
      */
     public void setState(int stateFlag) {
+        if (DEBUG_STATES) {
+            Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding "
+                    + convertToFlagNames(stateFlag) + " to " + convertToFlagNames(mState));
+        }
+
         int oldState = mState;
         mState = mState | stateFlag;
 
@@ -64,6 +73,11 @@
      * as a result.
      */
     public void clearState(int stateFlag) {
+        if (DEBUG_STATES) {
+            Log.d(TAG, "[" + System.identityHashCode(this) + "] Removing "
+                    + convertToFlagNames(stateFlag) + " from " + convertToFlagNames(mState));
+        }
+
         int oldState = mState;
         mState = mState & ~stateFlag;
         notifyStateChangeHandlers(oldState);
@@ -105,24 +119,14 @@
         return (mState & stateMask) == stateMask;
     }
 
-    private void debugNewState(int stateFlag) {
-        if (!DEBUG_STATES) {
-            return;
-        }
-
-        int state = getState();
-        StringJoiner currentStateStr = new StringJoiner(", ", "[", "]");
-        String stateFlagStr = "Unknown-" + stateFlag;
-        for (int i = 0; i < STATES.length; i++) {
-            if ((state & (i << i)) != 0) {
-                currentStateStr.add(STATES[i]);
-            }
-            if (stateFlag == (1 << i)) {
-                stateFlagStr = STATES[i] + " (" + stateFlag + ")";
+    private String convertToFlagNames(int flags) {
+        StringJoiner joiner = new StringJoiner(", ", "[", " (" + flags + ")]");
+        for (int i = 0; i < mStateNames.length; i++) {
+            if ((flags & (1 << i)) != 0) {
+                joiner.add(mStateNames[i]);
             }
         }
-        Log.d(TAG, "[" + System.identityHashCode(this) + "] Adding " + stateFlagStr + " to "
-                + currentStateStr);
+        return joiner.toString();
     }
 
 }
diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
similarity index 67%
rename from quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 84f16cb..67bfeaa 100644
--- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -24,6 +24,8 @@
 
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
+import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
 import android.annotation.TargetApi;
@@ -34,8 +36,8 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemClock;
+import android.os.Handler;
+import android.os.Looper;
 import android.view.Display;
 import android.view.MotionEvent;
 import android.view.Surface;
@@ -43,102 +45,132 @@
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
 
+import androidx.annotation.UiThread;
+
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Preconditions;
 import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.TraceHelper;
 import com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget;
+import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.quickstep.util.MotionPauseDetector;
 import com.android.quickstep.util.RecentsAnimationListenerSet;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.AssistDataReceiver;
 import com.android.systemui.shared.system.BackgroundExecutor;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
+import java.util.function.Consumer;
 
 /**
- * Touch consumer for handling events originating from an activity other than Launcher
+ * Input consumer for handling events originating from an activity other than Launcher
  */
 @TargetApi(Build.VERSION_CODES.P)
-public class OtherActivityTouchConsumer extends ContextWrapper implements TouchConsumer {
+public class OtherActivityInputConsumer extends ContextWrapper implements InputConsumer {
 
-    public static final String DOWN_EVT = "OtherActivityTouchConsumer.DOWN";
-    private static final String UP_EVT = "OtherActivityTouchConsumer.UP";
+    public static final String DOWN_EVT = "OtherActivityInputConsumer.DOWN";
+    private static final String UP_EVT = "OtherActivityInputConsumer.UP";
 
+    private final CachedEventDispatcher mRecentsViewDispatcher = new CachedEventDispatcher();
     private final RunningTaskInfo mRunningTask;
     private final RecentsModel mRecentsModel;
     private final Intent mHomeIntent;
     private final ActivityControlHelper mActivityControlHelper;
     private final OverviewCallbacks mOverviewCallbacks;
     private final TaskOverlayFactory mTaskOverlayFactory;
-    private final TouchInteractionLog mTouchInteractionLog;
     private final InputConsumerController mInputConsumer;
     private final SwipeSharedState mSwipeSharedState;
 
-    private final MotionEventQueue mEventQueue;
+    private final int mDisplayRotation;
+    private final Rect mStableInsets = new Rect();
+
+    private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
     private final MotionPauseDetector mMotionPauseDetector;
     private VelocityTracker mVelocityTracker;
 
+    private WindowTransformSwipeHandler mInteractionHandler;
+
     private final boolean mIsDeferredDownTarget;
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
     private int mActivePointerId = INVALID_POINTER_ID;
-    private boolean mPassedInitialSlop;
-    // Used for non-deferred gestures to determine when to start dragging
-    private int mQuickStepDragSlop;
-    private float mStartDisplacement;
-    private WindowTransformSwipeHandler mInteractionHandler;
-    private int mDisplayRotation;
-    private Rect mStableInsets = new Rect();
 
-    public OtherActivityTouchConsumer(Context base, RunningTaskInfo runningTaskInfo,
+    private final float mDragSlop;
+    private final float mTouchSlop;
+
+    // Slop used to check when we start moving window.
+    private boolean mPassedDragSlop;
+    // Slop used to determine when we say that the gesture has started.
+    private boolean mPassedTouchSlop;
+
+    // TODO: Start displacement should have both x and y
+    private float mStartDisplacement;
+
+    private Handler mMainThreadHandler;
+    private Runnable mCancelRecentsAnimationRunnable = () -> {
+        ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
+                true /* restoreHomeStackPosition */);
+    };
+
+    public OtherActivityInputConsumer(Context base, RunningTaskInfo runningTaskInfo,
             RecentsModel recentsModel, Intent homeIntent, ActivityControlHelper activityControl,
-            @HitTarget int downHitTarget, OverviewCallbacks overviewCallbacks,
+            boolean isDeferredDownTarget, OverviewCallbacks overviewCallbacks,
             TaskOverlayFactory taskOverlayFactory, InputConsumerController inputConsumer,
-            TouchInteractionLog touchInteractionLog, MotionEventQueue eventQueue,
+            Consumer<OtherActivityInputConsumer> onCompleteCallback,
             SwipeSharedState swipeSharedState) {
         super(base);
 
+        mMainThreadHandler = new Handler(Looper.getMainLooper());
         mRunningTask = runningTaskInfo;
         mRecentsModel = recentsModel;
         mHomeIntent = homeIntent;
 
         mMotionPauseDetector = new MotionPauseDetector(base);
-        mEventQueue = eventQueue;
+        mOnCompleteCallback = onCompleteCallback;
         mVelocityTracker = VelocityTracker.obtain();
 
         mActivityControlHelper = activityControl;
-        mIsDeferredDownTarget = activityControl.deferStartingActivity(downHitTarget);
+        mIsDeferredDownTarget = isDeferredDownTarget;
         mOverviewCallbacks = overviewCallbacks;
         mTaskOverlayFactory = taskOverlayFactory;
-        mTouchInteractionLog = touchInteractionLog;
-        mTouchInteractionLog.setTouchConsumer(this);
         mInputConsumer = inputConsumer;
         mSwipeSharedState = swipeSharedState;
+
+        Display display = getSystemService(WindowManager.class).getDefaultDisplay();
+        mDisplayRotation = display.getRotation();
+        WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+
+        mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
+        mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
+        // If active listener isn't null, we are continuing the previous gesture.
+        mPassedTouchSlop = mPassedDragSlop = mSwipeSharedState.getActiveListener() != null;
     }
 
     @Override
-    public void onShowOverviewFromAltTab() {
-        startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis());
-    }
-
-    @Override
-    public void accept(MotionEvent ev) {
+    public void onMotionEvent(MotionEvent ev) {
         if (mVelocityTracker == null) {
             return;
         }
+
+        // Proxy events to recents view
+        if (!isNavBarOnLeft() && !isNavBarOnRight()) {
+            if (mPassedDragSlop && mInteractionHandler != null
+                    && !mRecentsViewDispatcher.hasConsumer()) {
+                mRecentsViewDispatcher.setConsumer(mInteractionHandler.getRecentsViewDispatcher());
+            }
+            int edgeFlags = ev.getEdgeFlags();
+            ev.setEdgeFlags(edgeFlags | EDGE_NAV_BAR);
+            mRecentsViewDispatcher.dispatchEvent(ev);
+            ev.setEdgeFlags(edgeFlags);
+        }
+
         mVelocityTracker.addMovement(ev);
         if (ev.getActionMasked() == ACTION_POINTER_UP) {
             mVelocityTracker.clear();
             mMotionPauseDetector.clear();
         }
 
-        mTouchInteractionLog.addMotionEvent(ev);
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
                 RaceConditionTracker.onEvent(DOWN_EVT, ENTER);
@@ -146,9 +178,6 @@
                 mActivePointerId = ev.getPointerId(0);
                 mDownPos.set(ev.getX(), ev.getY());
                 mLastPos.set(mDownPos);
-                // If active listener isn't null, we are continuing the previous gesture.
-                mPassedInitialSlop = mSwipeSharedState.getActiveListener() != null;
-                mQuickStepDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
 
                 // Start the window animation on down to give more time for launcher to draw if the
                 // user didn't start the gesture over the back button
@@ -156,9 +185,6 @@
                     startTouchTrackingForWindowAnimation(ev.getEventTime());
                 }
 
-                Display display = getSystemService(WindowManager.class).getDefaultDisplay();
-                mDisplayRotation = display.getRotation();
-                WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
                 RaceConditionTracker.onEvent(DOWN_EVT, EXIT);
                 break;
             }
@@ -182,20 +208,40 @@
                 }
                 mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
                 float displacement = getDisplacement(ev);
-                if (!mPassedInitialSlop) {
+
+                if (!mPassedDragSlop) {
                     if (!mIsDeferredDownTarget) {
                         // Normal gesture, ensure we pass the drag slop before we start tracking
                         // the gesture
-                        if (Math.abs(displacement) > mQuickStepDragSlop) {
-                            mPassedInitialSlop = true;
+                        if (Math.abs(displacement) > mDragSlop) {
+                            mPassedDragSlop = true;
                             mStartDisplacement = displacement;
                         }
                     }
                 }
 
-                if (mPassedInitialSlop && mInteractionHandler != null) {
+                if (!mPassedTouchSlop) {
+                    if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
+                            mTouchSlop) {
+                        mPassedTouchSlop = true;
+
+                        TOUCH_INTERACTION_LOG.startQuickStep();
+                        if (mIsDeferredDownTarget) {
+                            // Deferred gesture, start the animation and gesture tracking once
+                            // we pass the actual touch slop
+                            startTouchTrackingForWindowAnimation(ev.getEventTime());
+                        }
+                        if (!mPassedDragSlop) {
+                            mPassedDragSlop = true;
+                            mStartDisplacement = displacement;
+                        }
+                        notifyGestureStarted();
+                    }
+                }
+
+                if (mPassedDragSlop && mInteractionHandler != null) {
                     // Move
-                    dispatchMotion(ev, displacement - mStartDisplacement, null);
+                    mInteractionHandler.updateDisplacement(displacement - mStartDisplacement);
 
                     if (FeatureFlags.SWIPE_HOME.get()) {
                         boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
@@ -220,14 +266,6 @@
         }
     }
 
-    private void dispatchMotion(MotionEvent ev, float displacement, @Nullable Float velocityX) {
-        mInteractionHandler.updateDisplacement(displacement);
-        boolean isLandscape = isNavBarOnLeft() || isNavBarOnRight();
-        if (!isLandscape) {
-            mInteractionHandler.dispatchMotionEventToRecentsView(ev, velocityX);
-        }
-    }
-
     private void notifyGestureStarted() {
         if (mInteractionHandler == null) {
             return;
@@ -250,12 +288,12 @@
     }
 
     private void startTouchTrackingForWindowAnimation(long touchTimeMs) {
-        mTouchInteractionLog.startRecentsAnimation();
+        TOUCH_INTERACTION_LOG.startRecentsAnimation();
 
         RecentsAnimationListenerSet listenerSet = mSwipeSharedState.getActiveListener();
         final WindowTransformSwipeHandler handler = new WindowTransformSwipeHandler(
                 mRunningTask, this, touchTimeMs, mActivityControlHelper,
-                listenerSet != null, mInputConsumer, mTouchInteractionLog);
+                listenerSet != null, mInputConsumer);
 
         // Preload the plan
         mRecentsModel.getTasks(null);
@@ -264,32 +302,16 @@
         mMotionPauseDetector.setOnMotionPauseListener(handler::onMotionPauseChanged);
         handler.initWhenReady();
 
-        TraceHelper.beginSection("RecentsController");
-
         if (listenerSet != null) {
             listenerSet.addListener(handler);
             mSwipeSharedState.applyActiveRecentsAnimationState(handler);
         } else {
-            AssistDataReceiver assistDataReceiver = !mTaskOverlayFactory.needAssist() ? null :
-                    new AssistDataReceiver() {
-                        @Override
-                        public void onHandleAssistData(Bundle bundle) {
-                            if (mInteractionHandler == null) {
-                                // Interaction is probably complete
-                                mRecentsModel.preloadAssistData(mRunningTask.id, bundle);
-                            } else if (handler == mInteractionHandler) {
-                                handler.onAssistDataReceived(bundle);
-                            }
-                        }
-                    };
-
             RecentsAnimationListenerSet newListenerSet =
                     mSwipeSharedState.newRecentsAnimationListenerSet();
             newListenerSet.addListener(handler);
             BackgroundExecutor.get().submit(
                     () -> ActivityManagerWrapper.getInstance().startRecentsActivity(
-                            mHomeIntent, assistDataReceiver, newListenerSet,
-                            null, null));
+                            mHomeIntent, null, newListenerSet, null, null));
         }
     }
 
@@ -298,7 +320,7 @@
      * the animation can still be running.
      */
     private void finishTouchTracking(MotionEvent ev) {
-        if (mPassedInitialSlop && mInteractionHandler != null) {
+        if (mPassedDragSlop && mInteractionHandler != null) {
 
             mVelocityTracker.computeCurrentVelocity(1000,
                     ViewConfiguration.get(this).getScaledMaximumFlingVelocity());
@@ -307,8 +329,7 @@
                     : isNavBarOnLeft() ? -velocityX
                             : mVelocityTracker.getYVelocity(mActivePointerId);
 
-            dispatchMotion(ev, getDisplacement(ev) - mStartDisplacement, velocityX);
-
+            mInteractionHandler.updateDisplacement(getDisplacement(ev) - mStartDisplacement);
             mInteractionHandler.onGestureEnded(velocity, velocityX);
         } else {
             // Since we start touch tracking on DOWN, we may reach this state without actually
@@ -316,18 +337,22 @@
             onConsumerAboutToBeSwitched();
             onInteractionGestureFinished();
 
-            // Also clean up in case the system has handled the UP and canceled the animation before
-            // we had a chance to start the recents animation. In such a case, we will not receive
-            ActivityManagerWrapper.getInstance().cancelRecentsAnimation(
-                    true /* restoreHomeStackPosition */);
+            // Cancel the recents animation if SysUI happens to handle UP before we have a chance
+            // to start the recents animation. In addition, workaround for b/126336729 by delaying
+            // the cancel of the animation for a period, in case SysUI is slow to handle UP and we
+            // handle DOWN & UP and move the home stack before SysUI can start the activity
+            mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
+            mMainThreadHandler.postDelayed(mCancelRecentsAnimationRunnable, 100);
         }
         mVelocityTracker.recycle();
         mVelocityTracker = null;
+        mMotionPauseDetector.clear();
     }
 
     @Override
     public void onConsumerAboutToBeSwitched() {
         Preconditions.assertUIThread();
+        mMainThreadHandler.removeCallbacks(mCancelRecentsAnimationRunnable);
         if (mInteractionHandler != null) {
             // The consumer is being switched while we are active. Set up the shared state to be
             // used by the next animation
@@ -348,7 +373,7 @@
         Preconditions.assertUIThread();
         removeListener();
         mInteractionHandler = null;
-        mEventQueue.onConsumerInactive(this);
+        mOnCompleteCallback.accept(this);
     }
 
     private void removeListener() {
@@ -358,53 +383,6 @@
         }
     }
 
-    @Override
-    public void onQuickScrubStart() {
-        if (!mPassedInitialSlop && mIsDeferredDownTarget && mInteractionHandler == null) {
-            // If we deferred starting the window animation on touch down, then
-            // start tracking now
-            startTouchTrackingForWindowAnimation(SystemClock.uptimeMillis());
-            mPassedInitialSlop = true;
-        }
-
-        mTouchInteractionLog.startQuickScrub();
-        if (mInteractionHandler != null) {
-            mInteractionHandler.onQuickScrubStart();
-        }
-        notifyGestureStarted();
-    }
-
-    @Override
-    public void onQuickScrubEnd() {
-        mTouchInteractionLog.endQuickScrub("onQuickScrubEnd");
-        if (mInteractionHandler != null) {
-            mInteractionHandler.onQuickScrubEnd();
-        }
-    }
-
-    @Override
-    public void onQuickScrubProgress(float progress) {
-        mTouchInteractionLog.setQuickScrubProgress(progress);
-        if (mInteractionHandler != null) {
-            mInteractionHandler.onQuickScrubProgress(progress);
-        }
-    }
-
-    @Override
-    public void onQuickStep(MotionEvent ev) {
-        mTouchInteractionLog.startQuickStep();
-        if (mIsDeferredDownTarget) {
-            // Deferred gesture, start the animation and gesture tracking once we pass the actual
-            // touch slop
-            startTouchTrackingForWindowAnimation(ev.getEventTime());
-        }
-        if (!mPassedInitialSlop) {
-            mPassedInitialSlop = true;
-            mStartDisplacement = getDisplacement(ev);
-        }
-        notifyGestureStarted();
-    }
-
     private float getDisplacement(MotionEvent ev) {
         float eventX = ev.getX();
         float eventY = ev.getY();
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
similarity index 71%
copy from quickstep/src/com/android/quickstep/OverviewCommandHelper.java
copy to quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index 5b4f673..6533c63 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -18,6 +18,8 @@
 import static com.android.systemui.shared.system.ActivityManagerWrapper
         .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.annotation.TargetApi;
 import android.content.Context;
@@ -31,6 +33,7 @@
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
 import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@@ -67,8 +70,12 @@
         mMainThreadExecutor.execute(new RecentsActivityCommand<>());
     }
 
-    public void onOverviewShown() {
-        mMainThreadExecutor.execute(new ShowRecentsCommand());
+    public void onOverviewShown(boolean triggeredFromAltTab) {
+        mMainThreadExecutor.execute(new ShowRecentsCommand(triggeredFromAltTab));
+    }
+
+    public void onOverviewHidden() {
+        mMainThreadExecutor.execute(new HideRecentsCommand());
     }
 
     public void onTip(int actionType, int viewType) {
@@ -78,10 +85,58 @@
 
     private class ShowRecentsCommand extends RecentsActivityCommand {
 
+        private final boolean mTriggeredFromAltTab;
+
+        ShowRecentsCommand(boolean triggeredFromAltTab) {
+            mTriggeredFromAltTab = triggeredFromAltTab;
+        }
+
         @Override
         protected boolean handleCommand(long elapsedTime) {
+            // TODO: Go to the next page if started from alt-tab.
             return mHelper.getVisibleRecentsView() != null;
         }
+
+        @Override
+        protected void onTransitionComplete() {
+            if (mTriggeredFromAltTab) {
+                RecentsView rv = (RecentsView) mHelper.getVisibleRecentsView();
+                if (rv == null) {
+                    return;
+                }
+
+                // Ensure that recents view has focus so that it receives the followup key inputs
+                TaskView taskView = rv.getNextTaskView();
+                if (taskView == null) {
+                    if (rv.getTaskViewCount() > 0) {
+                        taskView = (TaskView) rv.getPageAt(0);
+                        taskView.requestFocus();
+                    } else {
+                        rv.requestFocus();
+                    }
+                } else {
+                    taskView.requestFocus();
+                }
+            }
+        }
+    }
+
+    private class HideRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            RecentsView recents = (RecentsView) mHelper.getVisibleRecentsView();
+            if (recents == null) {
+                return false;
+            }
+            int currentPage = recents.getNextPage();
+            if (currentPage >= 0 && currentPage < recents.getTaskViewCount()) {
+                ((TaskView) recents.getPageAt(currentPage)).launchTask(true);
+            } else {
+                recents.startHome();
+            }
+            return true;
+        }
     }
 
     private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
@@ -114,7 +169,7 @@
                 return;
             }
 
-            if (mHelper.switchToRecentsIfVisible(true)) {
+            if (mHelper.switchToRecentsIfVisible(this::onTransitionComplete)) {
                 // If successfully switched, then return
                 return;
             }
@@ -162,7 +217,16 @@
 
             mListener.unregister();
 
-            return mAnimationProvider.createWindowAnimation(targetCompats);
+            AnimatorSet animatorSet = mAnimationProvider.createWindowAnimation(targetCompats);
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    onTransitionComplete();
+                }
+            });
+            return animatorSet;
         }
+
+        protected void onTransitionComplete() { }
     }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
new file mode 100644
index 0000000..28c4db4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -0,0 +1,155 @@
+/*
+ * 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 android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.graphics.PointF;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.views.BaseDragLayer;
+import com.android.quickstep.util.CachedEventDispatcher;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+
+/**
+ * Input consumer for handling touch on the recents/Launcher activity.
+ */
+public class OverviewInputConsumer<T extends BaseDraggingActivity>
+        implements InputConsumer {
+
+    private final CachedEventDispatcher mCachedEventDispatcher = new CachedEventDispatcher();
+    private final T mActivity;
+    private final BaseDragLayer mTarget;
+    private final int[] mLocationOnScreen = new int[2];
+    private final PointF mDownPos = new PointF();
+    private final int mTouchSlop;
+
+    private final boolean mStartingInActivityBounds;
+
+    private boolean mTrackingStarted = false;
+    private boolean mInvalidated = false;
+
+    OverviewInputConsumer(T activity, boolean startingInActivityBounds) {
+        mActivity = activity;
+        mTarget = activity.getDragLayer();
+        mTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
+        mStartingInActivityBounds = startingInActivityBounds;
+    }
+
+    @Override
+    public void onMotionEvent(MotionEvent ev) {
+        if (mInvalidated) {
+            return;
+        }
+        mCachedEventDispatcher.dispatchEvent(ev);
+        int action = ev.getActionMasked();
+        if (action == ACTION_DOWN) {
+            if (mStartingInActivityBounds) {
+                startTouchTracking(ev, false /* updateLocationOffset */,
+                        false /* closeActiveWindows */);
+                return;
+            }
+            mTrackingStarted = false;
+            mDownPos.set(ev.getX(), ev.getY());
+        } else if (!mTrackingStarted) {
+            switch (action) {
+                case ACTION_CANCEL:
+                case ACTION_UP:
+                    startTouchTracking(ev, true /* updateLocationOffset */,
+                            false /* closeActiveWindows */);
+                    break;
+                case ACTION_MOVE: {
+                    float displacement = mActivity.getDeviceProfile().isLandscape ?
+                            ev.getX() - mDownPos.x : ev.getY() - mDownPos.y;
+                    if (Math.abs(displacement) >= mTouchSlop) {
+                        // Start tracking only when mTouchSlop is crossed.
+                        startTouchTracking(ev, true /* updateLocationOffset */,
+                                true /* closeActiveWindows */);
+                    }
+                }
+            }
+        }
+
+        if (action == ACTION_UP || action == ACTION_CANCEL) {
+            mInvalidated = true;
+
+            // Set an empty consumer to that all the cached events are cleared
+            if (!mCachedEventDispatcher.hasConsumer()) {
+                mCachedEventDispatcher.setConsumer(motionEvent -> { });
+            }
+        }
+    }
+
+    @Override
+    public void onKeyEvent(KeyEvent ev) {
+        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+            mActivity.dispatchKeyEvent(ev);
+        }
+    }
+
+    private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset,
+            boolean closeActiveWindows) {
+        if (updateLocationOffset) {
+            mTarget.getLocationOnScreen(mLocationOnScreen);
+        }
+
+        if (closeActiveWindows) {
+            OverviewCallbacks.get(mActivity).closeAllWindows();
+            ActivityManagerWrapper.getInstance()
+                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+            TOUCH_INTERACTION_LOG.startQuickStep();
+        }
+
+        mTrackingStarted = true;
+        mCachedEventDispatcher.setConsumer(this::sendEvent);
+
+    }
+
+    private void sendEvent(MotionEvent ev) {
+        if (mInvalidated || !mTarget.verifyTouchDispatch(this, ev)) {
+            mInvalidated = true;
+            return;
+        }
+        int flags = ev.getEdgeFlags();
+        ev.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
+        ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
+        if (ev.getAction() == ACTION_DOWN) {
+            mTarget.onInterceptTouchEvent(ev);
+        }
+        mTarget.onTouchEvent(ev);
+        ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
+        ev.setEdgeFlags(flags);
+    }
+
+    public static InputConsumer newInstance(ActivityControlHelper activityHelper,
+            boolean startingInActivityBounds) {
+        BaseDraggingActivity activity = activityHelper.getCreatedActivity();
+        if (activity == null) {
+            return InputConsumer.NO_OP;
+        }
+        return new OverviewInputConsumer(activity, startingInActivityBounds);
+    }
+}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java
new file mode 100644
index 0000000..befeee0
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/QuickstepProcessInitializer.java
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserManager;
+import android.util.Log;
+
+import com.android.launcher3.BuildConfig;
+import com.android.launcher3.MainProcessInitializer;
+import com.android.systemui.shared.system.ThreadedRendererCompat;
+
+@SuppressWarnings("unused")
+public class QuickstepProcessInitializer extends MainProcessInitializer {
+
+    private static final String TAG = "QuickstepProcessInitializer";
+
+    public QuickstepProcessInitializer(Context context) { }
+
+    @Override
+    protected void init(Context context) {
+        // Workaround for b/120550382, an external app can cause the launcher process to start for
+        // a work profile user which we do not support. Disable the application immediately when we
+        // detect this to be the case.
+        UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        if (um.isManagedProfile()) {
+            PackageManager pm = context.getPackageManager();
+            pm.setApplicationEnabledSetting(context.getPackageName(),
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 0 /* flags */);
+            Log.w(TAG, "Disabling " + BuildConfig.APPLICATION_ID
+                    + ", unable to run in a managed profile");
+            return;
+        }
+
+        super.init(context);
+
+        // Elevate GPU priority for Quickstep and Remote animations.
+        ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
similarity index 62%
rename from quickstep/src/com/android/quickstep/RecentsActivity.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index b76a1ab..5decc3e 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
@@ -15,13 +15,12 @@
  */
 package com.android.quickstep;
 
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
-import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
-
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
-import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl
+        .STATUS_BAR_TRANSITION_DURATION;
+import static com.android.launcher3.QuickstepAppTransitionManagerImpl
+        .STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
@@ -29,23 +28,16 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.app.ActivityOptions;
-import android.content.Intent;
 import android.content.res.Configuration;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.view.View;
 
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
 import com.android.launcher3.LauncherAnimationRunner;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
-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;
@@ -56,46 +48,22 @@
 import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
 /**
- * A simple activity to show the recently launched tasks
+ * A recents activity that shows the recently launched tasks as swipable task cards.
+ * See {@link com.android.quickstep.views.RecentsView}.
  */
-public class RecentsActivity extends BaseDraggingActivity {
+public final class RecentsActivity extends BaseRecentsActivity {
 
     private Handler mUiHandler = new Handler(Looper.getMainLooper());
     private RecentsRootView mRecentsRootView;
     private FallbackRecentsView mFallbackRecentsView;
 
-    private Configuration mOldConfig;
-
     @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        mOldConfig = new Configuration(getResources().getConfiguration());
-        initDeviceProfile();
-
+    protected void initViews() {
         setContentView(R.layout.fallback_recents_activity);
         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);
-    }
-
-    @Override
-    public void onConfigurationChanged(Configuration newConfig) {
-        int diff = newConfig.diff(mOldConfig);
-        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
-            onHandleConfigChanged();
-        }
-        mOldConfig.setTo(newConfig);
-        super.onConfigurationChanged(newConfig);
     }
 
     @Override
@@ -110,16 +78,10 @@
         }
     }
 
-    private void onHandleConfigChanged() {
-        mUserEventDispatcher = null;
-        initDeviceProfile();
-
-        AbstractFloatingView.closeOpenViews(this, true,
-                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
-        dispatchDeviceProfileChanged();
-
+    @Override
+    protected void onHandleConfigChanged() {
+        super.onHandleConfigChanged();
         mRecentsRootView.setup();
-        reapplyUi();
     }
 
     @Override
@@ -127,15 +89,12 @@
         mRecentsRootView.dispatchInsets();
     }
 
-    private void initDeviceProfile() {
+    @Override
+    protected DeviceProfile createDeviceProfile() {
         DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
-
-        // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
-        // activity.
-        mDeviceProfile = (mRecentsRootView != null) && isInMultiWindowMode()
+        return (mRecentsRootView != null) && isInMultiWindowMode()
                 ? dp.getMultiWindowProfile(this, mRecentsRootView.getLastKnownSize())
-                : dp.copy(this);
-        onDeviceProfileInitiated();
+                : super.createDeviceProfile();
     }
 
     @Override
@@ -211,55 +170,4 @@
         super.onStart();
         mFallbackRecentsView.resetTaskVisuals();
     }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-
-        // Workaround for b/78520668, explicitly trim memory once UI is hidden
-        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
-    }
-
-    @Override
-    public void onEnterAnimationComplete() {
-        super.onEnterAnimationComplete();
-        UiFactory.onEnterAnimationComplete(this);
-    }
-
-    @Override
-    public void onTrimMemory(int level) {
-        super.onTrimMemory(level);
-        UiFactory.onTrimMemory(this, level);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        RecentsActivityTracker.onRecentsActivityNewIntent(this);
-    }
-
-    @Override
-    protected void onDestroy() {
-        super.onDestroy();
-        RecentsActivityTracker.onRecentsActivityDestroy(this);
-    }
-
-    @Override
-    public void onBackPressed() {
-        // TODO: Launch the task we came from
-        startHome();
-    }
-
-    public void startHome() {
-        startActivity(new Intent(Intent.ACTION_MAIN)
-                .addCategory(Intent.CATEGORY_HOME)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
-    }
-
-    @Override
-    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
-        super.dump(prefix, fd, writer, args);
-        writer.println(prefix + "Misc:");
-        dumpMisc(writer);
-    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
new file mode 100644
index 0000000..f0bc223
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsAnimationWrapper.java
@@ -0,0 +1,179 @@
+/*
+ * 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;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
+import androidx.annotation.UiThread;
+
+/**
+ * Wrapper around RecentsAnimationController to help with some synchronization
+ */
+public class RecentsAnimationWrapper {
+
+    // A list of callbacks to run when we receive the recents animation target. There are different
+    // than the state callbacks as these run on the current worker thread.
+    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
+
+    public SwipeAnimationTargetSet targetSet;
+
+    private boolean mWindowThresholdCrossed = false;
+
+    private final InputConsumerController mInputConsumerController;
+    private final Supplier<InputConsumer> mInputProxySupplier;
+
+    private InputConsumer mInputConsumer;
+    private boolean mTouchInProgress;
+
+    private boolean mFinishPending;
+
+    public RecentsAnimationWrapper(InputConsumerController inputConsumerController,
+            Supplier<InputConsumer> inputProxySupplier) {
+        mInputConsumerController = inputConsumerController;
+        mInputProxySupplier = inputProxySupplier;
+    }
+
+    @UiThread
+    public synchronized void setController(SwipeAnimationTargetSet targetSet) {
+        Preconditions.assertUIThread();
+        this.targetSet = targetSet;
+
+        if (targetSet == null) {
+            return;
+        }
+        targetSet.setWindowThresholdCrossed(mWindowThresholdCrossed);
+
+        if (!mCallbacks.isEmpty()) {
+            for (Runnable action : new ArrayList<>(mCallbacks)) {
+                action.run();
+            }
+            mCallbacks.clear();
+        }
+    }
+
+    public synchronized void runOnInit(Runnable action) {
+        if (targetSet == null) {
+            mCallbacks.add(action);
+        } else {
+            action.run();
+        }
+    }
+
+    /**
+     * @param onFinishComplete A callback that runs on the main thread after the animation
+     *                         controller has finished on the background thread.
+     */
+    @UiThread
+    public void finish(boolean toRecents, Runnable onFinishComplete) {
+        Preconditions.assertUIThread();
+        if (!toRecents) {
+            finishAndClear(false, onFinishComplete);
+        } else {
+            if (mTouchInProgress) {
+                mFinishPending = true;
+                // Execute the callback
+                if (onFinishComplete != null) {
+                    onFinishComplete.run();
+                }
+            } else {
+                finishAndClear(true, onFinishComplete);
+            }
+        }
+    }
+
+    private void finishAndClear(boolean toRecents, Runnable onFinishComplete) {
+        SwipeAnimationTargetSet controller = targetSet;
+        targetSet = null;
+        if (controller != null) {
+            controller.finishController(toRecents, onFinishComplete);
+        }
+    }
+
+    public void enableInputConsumer() {
+        if (targetSet != null) {
+            targetSet.enableInputConsumer();
+        }
+    }
+
+    /**
+     * Indicates that the gesture has crossed the window boundary threshold and system UI can be
+     * update the represent the window behind
+     */
+    public void setWindowThresholdCrossed(boolean windowThresholdCrossed) {
+        if (mWindowThresholdCrossed != windowThresholdCrossed) {
+            mWindowThresholdCrossed = windowThresholdCrossed;
+            if (targetSet != null) {
+                targetSet.setWindowThresholdCrossed(windowThresholdCrossed);
+            }
+        }
+    }
+
+    public void enableInputProxy() {
+        mInputConsumerController.setInputListener(this::onInputConsumerEvent);
+    }
+
+    private boolean onInputConsumerEvent(InputEvent ev) {
+        if (ev instanceof MotionEvent) {
+            onInputConsumerMotionEvent((MotionEvent) ev);
+        } else if (ev instanceof KeyEvent) {
+            if (mInputConsumer == null) {
+                mInputConsumer = mInputProxySupplier.get();
+            }
+            mInputConsumer.onKeyEvent((KeyEvent) ev);
+            return true;
+        }
+        return false;
+    }
+
+    private boolean onInputConsumerMotionEvent(MotionEvent ev) {
+        int action = ev.getAction();
+        if (action == ACTION_DOWN) {
+            mTouchInProgress = true;
+            if (mInputConsumer == null) {
+                mInputConsumer = mInputProxySupplier.get();
+            }
+        } else if (action == ACTION_CANCEL || action == ACTION_UP) {
+            // Finish any pending actions
+            mTouchInProgress = false;
+            if (mFinishPending) {
+                mFinishPending = false;
+                finishAndClear(true /* toRecents */, null);
+            }
+        }
+        if (mInputConsumer != null) {
+            mInputConsumer.onMotionEvent(ev);
+        }
+
+        return true;
+    }
+
+    public SwipeAnimationTargetSet getController() {
+        return targetSet;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/SwipeSharedState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
similarity index 70%
rename from quickstep/src/com/android/quickstep/SwipeSharedState.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
index 15914ba..7c6638a 100644
--- a/quickstep/src/com/android/quickstep/SwipeSharedState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/SwipeSharedState.java
@@ -15,6 +15,9 @@
  */
 package com.android.quickstep;
 
+import android.util.Log;
+
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.Preconditions;
 import com.android.quickstep.util.RecentsAnimationListenerSet;
 import com.android.quickstep.util.SwipeAnimationTargetSet;
@@ -25,23 +28,35 @@
  */
 public class SwipeSharedState implements SwipeAnimationListener {
 
+    private final OverviewComponentObserver mOverviewComponentObserver;
+
     private RecentsAnimationListenerSet mRecentsAnimationListener;
     private SwipeAnimationTargetSet mLastAnimationTarget;
+
     private boolean mLastAnimationCancelled = false;
+    private boolean mLastAnimationRunning = false;
 
     public boolean canGestureBeContinued;
     public boolean goingToLauncher;
 
+    public SwipeSharedState(OverviewComponentObserver overviewComponentObserver) {
+        mOverviewComponentObserver = overviewComponentObserver;
+    }
+
     @Override
     public final void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet) {
         mLastAnimationTarget = targetSet;
+
         mLastAnimationCancelled = false;
+        mLastAnimationRunning = true;
     }
 
     @Override
     public final void onRecentsAnimationCanceled() {
         mLastAnimationTarget = null;
+
         mLastAnimationCancelled = true;
+        mLastAnimationRunning = false;
     }
 
     private void clearListenerState() {
@@ -51,12 +66,31 @@
         mRecentsAnimationListener = null;
         mLastAnimationTarget = null;
         mLastAnimationCancelled = false;
+        mLastAnimationRunning = false;
+    }
+
+    private void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet) {
+        if (mLastAnimationTarget == targetSet) {
+            mLastAnimationRunning = false;
+        }
     }
 
     public RecentsAnimationListenerSet newRecentsAnimationListenerSet() {
         Preconditions.assertUIThread();
+
+        if (mLastAnimationRunning) {
+            String msg = "New animation started before completing old animation";
+            if (FeatureFlags.IS_DOGFOOD_BUILD) {
+                throw new IllegalArgumentException(msg);
+            } else {
+                Log.e("SwipeSharedState", msg, new Exception());
+            }
+        }
+
         clearListenerState();
-        mRecentsAnimationListener = new RecentsAnimationListenerSet();
+        mRecentsAnimationListener = new RecentsAnimationListenerSet(mOverviewComponentObserver
+                .getActivityControlHelper().shouldMinimizeSplitScreen(),
+                this::onSwipeAnimationFinished);
         mRecentsAnimationListener.addListener(this);
         return mRecentsAnimationListener;
     }
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
similarity index 95%
rename from quickstep/src/com/android/quickstep/TaskOverlayFactory.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index cc49d46..d979c99 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -31,8 +31,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import androidx.annotation.AnyThread;
-
 /**
  * Factory class to create and add an overlays on the TaskView
  */
@@ -51,11 +49,6 @@
             new MainThreadInitializedObject<>(c -> Overrides.getObject(TaskOverlayFactory.class,
                     c, R.string.task_overlay_factory_class));
 
-    @AnyThread
-    public boolean needAssist() {
-        return false;
-    }
-
     public TaskOverlay createOverlay(View thumbnailView) {
         return new TaskOverlay();
     }
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/TaskSystemShortcut.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/TaskSystemShortcut.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
new file mode 100644
index 0000000..4526d67
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -0,0 +1,156 @@
+/*
+ * 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.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
+import android.graphics.RectF;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+
+/**
+ * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
+ */
+public final class TaskViewUtils {
+
+    private TaskViewUtils() {}
+
+    /**
+     * Try to find a TaskView that corresponds with the component of the launched view.
+     *
+     * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
+     * Otherwise, we will assume we are using a normal app transition, but it's possible that the
+     * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
+     */
+    public static TaskView findTaskViewToLaunch(
+            BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
+        RecentsView recentsView = activity.getOverviewPanel();
+        if (v instanceof TaskView) {
+            TaskView taskView = (TaskView) v;
+            return recentsView.isTaskViewVisible(taskView) ? taskView : null;
+        }
+
+        // It's possible that the launched view can still be resolved to a visible task view, check
+        // the task id of the opening task and see if we can find a match.
+        if (v.getTag() instanceof ItemInfo) {
+            ItemInfo itemInfo = (ItemInfo) v.getTag();
+            ComponentName componentName = itemInfo.getTargetComponent();
+            int userId = itemInfo.user.getIdentifier();
+            if (componentName != null) {
+                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
+                    TaskView taskView = recentsView.getTaskViewAt(i);
+                    if (recentsView.isTaskViewVisible(taskView)) {
+                        Task.TaskKey key = taskView.getTask().key;
+                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
+                            return taskView;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (targets == null) {
+            return null;
+        }
+        // Resolve the opening task id
+        int openingTaskId = -1;
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == MODE_OPENING) {
+                openingTaskId = target.taskId;
+                break;
+            }
+        }
+
+        // If there is no opening task id, fall back to the normal app icon launch animation
+        if (openingTaskId == -1) {
+            return null;
+        }
+
+        // If the opening task id is not currently visible in overview, then fall back to normal app
+        // icon launch animation
+        TaskView taskView = recentsView.getTaskView(openingTaskId);
+        if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
+            return null;
+        }
+        return taskView;
+    }
+
+    /**
+     * @return Animator that controls the window of the opening targets for the recents launch
+     * animation.
+     */
+    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
+                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
+
+        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+
+            // Defer fading out the view until after the app window gets faded in
+            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
+            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
+
+            final RemoteAnimationTargetSet mTargetSet;
+
+            final RectF mThumbnailRect;
+
+            {
+                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
+                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
+
+                inOutHelper.prepareAnimation(true /* isOpening */);
+                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
+                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
+
+                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
+                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
+                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
+            }
+
+            @Override
+            public void onUpdate(float percent) {
+                params.setProgress(1 - percent);
+                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, params);
+                if (!skipViewChanges) {
+                    float scale = taskBounds.width() / mThumbnailRect.width();
+                    v.setScaleX(scale);
+                    v.setScaleY(scale);
+                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
+                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
+                    v.setAlpha(mViewAlpha.value);
+                }
+            }
+        });
+        return appAnimator;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionLog.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
similarity index 80%
rename from quickstep/src/com/android/quickstep/TouchInteractionLog.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
index 053efbb..4b660d4 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionLog.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionLog.java
@@ -23,7 +23,7 @@
 import java.util.LinkedList;
 
 /**
- * Keeps track of debugging logs for a particular quickstep/scrub gesture.
+ * Keeps track of debugging logs for a particular quickstep gesture.
  */
 public class TouchInteractionLog {
 
@@ -42,7 +42,7 @@
         getCurrentLog().add("[" + mDateFormat.format(mCalendar.getTime()) + "]");
     }
 
-    public void setTouchConsumer(TouchConsumer consumer) {
+    public void setInputConsumer(InputConsumer consumer) {
         getCurrentLog().add("tc=" + consumer.getClass().getSimpleName());
     }
 
@@ -54,18 +54,6 @@
         getCurrentLog().add("qstStart");
     }
 
-    public void startQuickScrub() {
-        getCurrentLog().add("qsStart");
-    }
-
-    public void setQuickScrubProgress(float progress) {
-        getCurrentLog().add("qsP=" + progress);
-    }
-
-    public void endQuickScrub(String reason) {
-        getCurrentLog().add("qsEnd=" + reason);
-    }
-
     public void startRecentsAnimation() {
         getCurrentLog().add("raStart");
     }
@@ -82,14 +70,6 @@
         getCurrentLog().add("raFinish=" + toHome);
     }
 
-    public void launchTaskStart() {
-        getCurrentLog().add("launchStart");
-    }
-
-    public void launchTaskEnd(boolean result) {
-        getCurrentLog().add("launchEnd=" + result);
-    }
-
     public void dump(PrintWriter pw) {
         pw.println("TouchInteractionLog {");
         for (ArrayList<String> gesture : mGestureLogs) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
new file mode 100644
index 0000000..ddf3ad5
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017 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 android.view.MotionEvent.ACTION_DOWN;
+
+import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_CHANNEL;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Region;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Choreographer;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.util.LooperExecutor;
+import com.android.launcher3.util.UiThreadHelper;
+import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
+import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Service connected by system-UI for handling touch interaction.
+ */
+@TargetApi(Build.VERSION_CODES.O)
+public class TouchInteractionService extends Service {
+
+    public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor();
+    public static final LooperExecutor BACKGROUND_EXECUTOR =
+            new LooperExecutor(UiThreadHelper.getBackgroundLooper());
+
+    public static final TouchInteractionLog TOUCH_INTERACTION_LOG = new TouchInteractionLog();
+
+    public static final int EDGE_NAV_BAR = 1 << 8;
+
+    private static final String TAG = "TouchInteractionService";
+
+    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
+
+        public void onActiveNavBarRegionChanges(Region region) {
+            mActiveNavBarRegion = region;
+        }
+
+        public void onInitialize(Bundle bundle) {
+            mISystemUiProxy = ISystemUiProxy.Stub
+                    .asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
+            mRecentsModel.setSystemUiProxy(mISystemUiProxy);
+            mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
+
+            disposeEventHandlers();
+            mInputEventReceiver = InputChannelCompat.fromBundle(bundle, KEY_EXTRA_INPUT_CHANNEL,
+                    Looper.getMainLooper(), mMainChoreographer,
+                    TouchInteractionService.this::onInputEvent);
+        }
+
+        @Override
+        public void onOverviewToggle() {
+            mOverviewCommandHelper.onOverviewToggle();
+        }
+
+        @Override
+        public void onOverviewShown(boolean triggeredFromAltTab) {
+            mOverviewCommandHelper.onOverviewShown(triggeredFromAltTab);
+        }
+
+        @Override
+        public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
+            if (triggeredFromAltTab && !triggeredFromHomeKey) {
+                // onOverviewShownFromAltTab hides the overview and ends at the target app
+                mOverviewCommandHelper.onOverviewHidden();
+            }
+        }
+
+        @Override
+        public void onTip(int actionType, int viewType) {
+            mOverviewCommandHelper.onTip(actionType, viewType);
+        }
+
+        /** Deprecated methods **/
+        public void onQuickStep(MotionEvent motionEvent) { }
+
+        public void onQuickScrubEnd() { }
+
+        public void onQuickScrubProgress(float progress) { }
+
+        public void onQuickScrubStart() { }
+
+        public void onPreMotionEvent(int downHitTarget) { }
+
+        public void onMotionEvent(MotionEvent ev) {
+            if (mDeprecatedDispatcher == null) {
+                ev.recycle();
+            } else {
+                mDeprecatedDispatcher.dispatch(ev);
+            }
+        }
+
+        public void onBind(ISystemUiProxy iSystemUiProxy) {
+            mISystemUiProxy = iSystemUiProxy;
+            mRecentsModel.setSystemUiProxy(mISystemUiProxy);
+            mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
+
+            // On Bind is received before onInitialize which will dispose these handlers
+            disposeEventHandlers();
+            Pair<InputEventDispatcher, InputEventReceiver> pair = InputChannelCompat.createPair(
+                    "sysui-callbacks", Looper.getMainLooper(), mMainChoreographer,
+                    TouchInteractionService.this::onInputEvent);
+            mDeprecatedDispatcher = pair.first;
+            mInputEventReceiver = pair.second;
+
+        }
+    };
+
+    private static boolean sConnected = false;
+
+    public static boolean isConnected() {
+        return sConnected;
+    }
+
+    private ActivityManagerWrapper mAM;
+    private RecentsModel mRecentsModel;
+    private ISystemUiProxy mISystemUiProxy;
+    private OverviewCommandHelper mOverviewCommandHelper;
+    private OverviewComponentObserver mOverviewComponentObserver;
+    private OverviewInteractionState mOverviewInteractionState;
+    private OverviewCallbacks mOverviewCallbacks;
+    private TaskOverlayFactory mTaskOverlayFactory;
+    private InputConsumerController mInputConsumer;
+    private SwipeSharedState mSwipeSharedState;
+
+    private InputConsumer mConsumer = InputConsumer.NO_OP;
+    private Choreographer mMainChoreographer;
+
+    private InputEventReceiver mInputEventReceiver;
+    private Region mActiveNavBarRegion = new Region();
+
+    private InputEventDispatcher mDeprecatedDispatcher;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        mAM = ActivityManagerWrapper.getInstance();
+        mRecentsModel = RecentsModel.INSTANCE.get(this);
+        mOverviewComponentObserver = new OverviewComponentObserver(this);
+        mMainChoreographer = Choreographer.getInstance();
+
+        mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
+        mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
+        mOverviewCallbacks = OverviewCallbacks.get(this);
+        mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
+        mSwipeSharedState = new SwipeSharedState(mOverviewComponentObserver);
+        mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
+        mInputConsumer.registerInputConsumer();
+
+        sConnected = true;
+
+        // Temporarily disable model preload
+        // new ModelPreload().start(this);
+    }
+
+    @Override
+    public void onDestroy() {
+        mInputConsumer.unregisterInputConsumer();
+        mOverviewComponentObserver.onDestroy();
+        disposeEventHandlers();
+        sConnected = false;
+        super.onDestroy();
+    }
+
+    private void disposeEventHandlers() {
+        if (mInputEventReceiver != null) {
+            mInputEventReceiver.dispose();
+            mInputEventReceiver = null;
+        }
+        if (mDeprecatedDispatcher != null) {
+            mDeprecatedDispatcher.dispose();
+            mDeprecatedDispatcher = null;
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "Touch service connected");
+        return mMyBinder;
+    }
+
+    private void onInputEvent(InputEvent ev) {
+        if (!(ev instanceof MotionEvent)) {
+            Log.e(TAG, "Unknown event " + ev);
+            return;
+        }
+        MotionEvent event = (MotionEvent) ev;
+        if (event.getAction() == ACTION_DOWN) {
+            TOUCH_INTERACTION_LOG.prepareForNewGesture();
+            boolean useSharedState = mConsumer.isActive();
+            mConsumer.onConsumerAboutToBeSwitched();
+            mConsumer = newConsumer(useSharedState, event);
+            TOUCH_INTERACTION_LOG.setInputConsumer(mConsumer);
+        }
+        TOUCH_INTERACTION_LOG.addMotionEvent(event);
+
+        mConsumer.onMotionEvent(event);
+    }
+
+    private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
+        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
+        if (!useSharedState) {
+            mSwipeSharedState.clearAllState();
+        }
+
+        if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
+            return InputConsumer.NO_OP;
+        } else if (mSwipeSharedState.goingToLauncher ||
+                mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
+            return OverviewInputConsumer.newInstance(
+                    mOverviewComponentObserver.getActivityControlHelper(), false);
+        } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
+                mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
+            return OverviewInputConsumer.newInstance(
+                    mOverviewComponentObserver.getActivityControlHelper(), false);
+        } else {
+            ActivityControlHelper activityControl =
+                    mOverviewComponentObserver.getActivityControlHelper();
+            boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
+            return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
+                    mOverviewComponentObserver.getOverviewIntent(), activityControl,
+                    shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
+                    this::onConsumerInactive, mSwipeSharedState);
+        }
+    }
+
+    /**
+     * To be called by the consumer when it's no longer active.
+     */
+    private void onConsumerInactive(InputConsumer caller) {
+        if (mConsumer == caller) {
+            mConsumer = InputConsumer.NO_OP;
+        }
+    }
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        TOUCH_INTERACTION_LOG.dump(pw);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
similarity index 70%
rename from quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index bc4e094..4d0136e 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -25,16 +25,14 @@
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
-import static com.android.launcher3.config.FeatureFlags.SWIPE_HOME;
 import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.HIDE;
 import static com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState.PEEK;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
-import static com.android.quickstep.QuickScrubController.QUICK_SWITCH_FROM_APP_START_DURATION;
-import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
-import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
+import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
 import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
 import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.HOME;
 import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.LAST_TASK;
 import static com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget.NEW_TASK;
@@ -50,28 +48,23 @@
 import android.annotation.TargetApi;
 import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
-import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
-import android.os.UserHandle;
 import android.view.HapticFeedbackConstants;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.View.OnApplyWindowInsetsListener;
 import android.view.ViewTreeObserver.OnDrawListener;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
@@ -81,7 +74,6 @@
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -90,110 +82,93 @@
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.util.RaceConditionTracker;
 import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
 import com.android.quickstep.ActivityControlHelper.AnimationFactory;
 import com.android.quickstep.ActivityControlHelper.AnimationFactory.ShelfAnimState;
-import com.android.quickstep.ActivityControlHelper.LayoutListener;
-import com.android.quickstep.TouchConsumer.InteractionType;
-import com.android.quickstep.TouchInteractionService.OverviewTouchConsumer;
+import com.android.quickstep.ActivityControlHelper.HomeAnimationFactory;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.RemoteAnimationTargetSet;
 import com.android.quickstep.util.SwipeAnimationTargetSet;
 import com.android.quickstep.util.SwipeAnimationTargetSet.SwipeAnimationListener;
-import com.android.quickstep.util.TransformedRect;
+import com.android.quickstep.views.LiveTileOverlay;
 import com.android.quickstep.views.RecentsView;
 import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.recents.utilities.RectFEvaluator;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.InputConsumerController;
 import com.android.systemui.shared.system.LatencyTrackerCompat;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 import com.android.systemui.shared.system.WindowCallbacksCompat;
 
 import java.util.function.BiFunction;
+import java.util.function.Consumer;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.UiThread;
 
 @TargetApi(Build.VERSION_CODES.O)
 public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
-        implements SwipeAnimationListener {
+        implements SwipeAnimationListener, OnApplyWindowInsetsListener {
     private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
 
+    private static final String[] STATE_NAMES = DEBUG_STATES ? new String[19] : null;
+
+    private static int getFlagForIndex(int index, String name) {
+        if (DEBUG_STATES) {
+            STATE_NAMES[index] = name;
+        }
+        return 1 << index;
+    }
+
     // Launcher UI related states
-    private static final int STATE_LAUNCHER_PRESENT = 1 << 0;
-    private static final int STATE_LAUNCHER_STARTED = 1 << 1;
-    private static final int STATE_LAUNCHER_DRAWN = 1 << 2;
-    private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE = 1 << 3;
+    private static final int STATE_LAUNCHER_PRESENT = getFlagForIndex(0, "STATE_LAUNCHER_PRESENT");
+    private static final int STATE_LAUNCHER_STARTED = getFlagForIndex(1, "STATE_LAUNCHER_STARTED");
+    private static final int STATE_LAUNCHER_DRAWN = getFlagForIndex(2, "STATE_LAUNCHER_DRAWN");
+    private static final int STATE_ACTIVITY_MULTIPLIER_COMPLETE =
+            getFlagForIndex(3, "STATE_ACTIVITY_MULTIPLIER_COMPLETE");
 
     // Internal initialization states
-    private static final int STATE_APP_CONTROLLER_RECEIVED = 1 << 4;
+    private static final int STATE_APP_CONTROLLER_RECEIVED =
+            getFlagForIndex(4, "STATE_APP_CONTROLLER_RECEIVED");
 
     // Interaction finish states
-    private static final int STATE_SCALED_CONTROLLER_HOME = 1 << 5;
-    private static final int STATE_SCALED_CONTROLLER_RECENTS = 1 << 6;
-    private static final int STATE_SCALED_CONTROLLER_LAST_TASK = 1 << 7;
+    private static final int STATE_SCALED_CONTROLLER_HOME =
+            getFlagForIndex(5, "STATE_SCALED_CONTROLLER_HOME");
+    private static final int STATE_SCALED_CONTROLLER_RECENTS =
+            getFlagForIndex(6, "STATE_SCALED_CONTROLLER_RECENTS");
+    private static final int STATE_SCALED_CONTROLLER_LAST_TASK =
+            getFlagForIndex(7, "STATE_SCALED_CONTROLLER_LAST_TASK");
 
-    private static final int STATE_HANDLER_INVALIDATED = 1 << 8;
-    private static final int STATE_GESTURE_STARTED_QUICKSTEP = 1 << 9;
-    private static final int STATE_GESTURE_STARTED_QUICKSCRUB = 1 << 10;
-    private static final int STATE_GESTURE_CANCELLED = 1 << 11;
-    private static final int STATE_GESTURE_COMPLETED = 1 << 12;
+    private static final int STATE_HANDLER_INVALIDATED =
+            getFlagForIndex(8, "STATE_HANDLER_INVALIDATED");
+    private static final int STATE_GESTURE_STARTED =
+            getFlagForIndex(9, "STATE_GESTURE_STARTED");
+    private static final int STATE_GESTURE_CANCELLED =
+            getFlagForIndex(10, "STATE_GESTURE_CANCELLED");
+    private static final int STATE_GESTURE_COMPLETED =
+            getFlagForIndex(11, "STATE_GESTURE_COMPLETED");
 
-    // States for quick switch/scrub
-    private static final int STATE_CURRENT_TASK_FINISHED = 1 << 13;
-    private static final int STATE_QUICK_SCRUB_START = 1 << 14;
-    private static final int STATE_QUICK_SCRUB_END = 1 << 15;
+    private static final int STATE_CAPTURE_SCREENSHOT =
+            getFlagForIndex(12, "STATE_CAPTURE_SCREENSHOT");
+    private static final int STATE_SCREENSHOT_CAPTURED =
+            getFlagForIndex(13, "STATE_SCREENSHOT_CAPTURED");
+    private static final int STATE_SCREENSHOT_VIEW_SHOWN =
+            getFlagForIndex(14, "STATE_SCREENSHOT_VIEW_SHOWN");
 
-    private static final int STATE_CAPTURE_SCREENSHOT = 1 << 16;
-    private static final int STATE_SCREENSHOT_CAPTURED = 1 << 17;
-    private static final int STATE_SCREENSHOT_VIEW_SHOWN = 1 << 18;
-
-    private static final int STATE_RESUME_LAST_TASK = 1 << 19;
-    private static final int STATE_START_NEW_TASK = 1 << 20;
-    private static final int STATE_ASSIST_DATA_RECEIVED = 1 << 21;
-
+    private static final int STATE_RESUME_LAST_TASK =
+            getFlagForIndex(15, "STATE_RESUME_LAST_TASK");
+    private static final int STATE_START_NEW_TASK =
+            getFlagForIndex(16, "STATE_START_NEW_TASK");
+    private static final int STATE_CURRENT_TASK_FINISHED =
+            getFlagForIndex(17, "STATE_CURRENT_TASK_FINISHED");
 
     private static final int LAUNCHER_UI_STATES =
             STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN | STATE_ACTIVITY_MULTIPLIER_COMPLETE
             | STATE_LAUNCHER_STARTED;
 
-    private static final int LONG_SWIPE_ENTER_STATE =
-            STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_STARTED
-                    | STATE_APP_CONTROLLER_RECEIVED;
-
-    private static final int LONG_SWIPE_START_STATE =
-            STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_LAUNCHER_STARTED
-                    | STATE_APP_CONTROLLER_RECEIVED | STATE_SCREENSHOT_CAPTURED;
-
-    private static final int QUICK_SCRUB_START_UI_STATE = STATE_LAUNCHER_STARTED
-            | STATE_QUICK_SCRUB_START | STATE_APP_CONTROLLER_RECEIVED;
-
     // For debugging, keep in sync with above states
-    public static final String[] STATES = new String[] {
-            "STATE_LAUNCHER_PRESENT",
-            "STATE_LAUNCHER_STARTED",
-            "STATE_LAUNCHER_DRAWN",
-            "STATE_ACTIVITY_MULTIPLIER_COMPLETE",
-            "STATE_APP_CONTROLLER_RECEIVED",
-            "STATE_SCALED_CONTROLLER_HOME",
-            "STATE_SCALED_CONTROLLER_RECENTS",
-            "STATE_SCALED_CONTROLLER_LAST_TASK",
-            "STATE_HANDLER_INVALIDATED",
-            "STATE_GESTURE_STARTED_QUICKSTEP",
-            "STATE_GESTURE_STARTED_QUICKSCRUB",
-            "STATE_GESTURE_CANCELLED",
-            "STATE_GESTURE_COMPLETED",
-            "STATE_CURRENT_TASK_FINISHED",
-            "STATE_QUICK_SCRUB_START",
-            "STATE_QUICK_SCRUB_END",
-            "STATE_CAPTURE_SCREENSHOT",
-            "STATE_SCREENSHOT_CAPTURED",
-            "STATE_SCREENSHOT_VIEW_SHOWN",
-            "STATE_RESUME_LAST_TASK",
-            "STATE_START_NEW_TASK",
-            "STATE_ASSIST_DATA_RECEIVED",
-    };
 
     enum GestureEndTarget {
         HOME(1, STATE_SCALED_CONTROLLER_HOME, true, false, ContainerType.WORKSPACE),
@@ -214,11 +189,15 @@
             this.containerType = containerType;
         }
 
-        // 0 is app, 1 is overview
+        /** 0 is app, 1 is overview */
         public final float endShift;
+        /** The state to apply when we reach this final target */
         public final int endState;
+        /** Whether the target is in the launcher activity */
         public final boolean isLauncher;
+        /** Whether the user can start a new gesture while this one is finishing */
         public final boolean canBeContinued;
+        /** Used to log where the user ended up after the gesture ends */
         public final int containerType;
     }
 
@@ -257,7 +236,6 @@
     private final Context mContext;
     private final ActivityControlHelper<T> mActivityControlHelper;
     private final ActivityInitListener mActivityInitListener;
-    private final TouchInteractionLog mTouchInteractionLog;
 
     private final int mRunningTaskId;
     private final RunningTaskInfo mRunningTaskInfo;
@@ -267,36 +245,25 @@
     private AnimatorPlaybackController mLauncherTransitionController;
 
     private T mActivity;
-    private LayoutListener mLayoutListener;
     private RecentsView mRecentsView;
     private SyncRtSurfaceTransactionApplierCompat mSyncTransactionApplier;
-    private QuickScrubController mQuickScrubController;
-    private AnimationFactory mAnimationFactory = (t, i) -> { };
+    private AnimationFactory mAnimationFactory = (t) -> { };
+    private LiveTileOverlay mLiveTileOverlay = new LiveTileOverlay();
 
     private boolean mWasLauncherAlreadyVisible;
 
     private boolean mPassedOverviewThreshold;
     private boolean mGestureStarted;
     private int mLogAction = Touch.SWIPE;
-    private float mCurrentQuickScrubProgress;
-    private boolean mQuickScrubBlocked;
-
-    private @InteractionType int mInteractionType = INTERACTION_NORMAL;
 
     private final RecentsAnimationWrapper mRecentsAnimationWrapper;
 
     private final long mTouchTimeMs;
     private long mLauncherFrameDrawnTime;
 
-    private boolean mLongSwipeMode = false;
-    private float mLongSwipeDisplacement = 0;
-    private LongSwipeHelper mLongSwipeController;
-
-    private Bundle mAssistData;
-
     WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context,
             long touchTimeMs, ActivityControlHelper<T> controller, boolean continuingLastGesture,
-            InputConsumerController inputConsumer, TouchInteractionLog touchInteractionLog) {
+            InputConsumerController inputConsumer) {
         mContext = context;
         mRunningTaskInfo = runningTaskInfo;
         mRunningTaskId = runningTaskInfo.id;
@@ -305,9 +272,8 @@
         mActivityInitListener = mActivityControlHelper
                 .createActivityInitListener(this::onActivityInit);
         mContinuingLastGesture = continuingLastGesture;
-        mTouchInteractionLog = touchInteractionLog;
         mRecentsAnimationWrapper = new RecentsAnimationWrapper(inputConsumer,
-                this::createNewTouchProxyHandler);
+                this::createNewInputProxyHandler);
         mClipAnimationHelper = new ClipAnimationHelper(context);
         mTransformParams = new ClipAnimationHelper.TransformParams();
 
@@ -315,24 +281,20 @@
     }
 
     private void initStateCallbacks() {
-        mStateCallback = new MultiStateCallback();
+        mStateCallback = new MultiStateCallback(STATE_NAMES);
 
         // Re-setup the recents UI when gesture starts, as the state could have been changed during
         // that time by a previous window transition.
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_GESTURE_STARTED_QUICKSTEP,
+        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_GESTURE_STARTED,
                 this::setupRecentsViewUi);
 
-        mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED_QUICKSCRUB,
-                this::initializeLauncherAnimationController);
-        mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED_QUICKSTEP,
+        mStateCallback.addCallback(STATE_LAUNCHER_DRAWN | STATE_GESTURE_STARTED,
                 this::initializeLauncherAnimationController);
 
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_DRAWN,
                 this::launcherFrameDrawn);
 
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED_QUICKSTEP,
-                this::notifyGestureStartedAsync);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED_QUICKSCRUB,
+        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_GESTURE_STARTED,
                 this::notifyGestureStartedAsync);
 
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_LAUNCHER_STARTED
@@ -368,13 +330,8 @@
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
                         | STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
                         | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
-                        | STATE_GESTURE_STARTED_QUICKSTEP,
+                        | STATE_GESTURE_STARTED,
                 this::setupLauncherUiAfterSwipeUpAnimation);
-        mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_APP_CONTROLLER_RECEIVED
-                        | STATE_ACTIVITY_MULTIPLIER_COMPLETE | STATE_SCALED_CONTROLLER_RECENTS
-                        | STATE_CURRENT_TASK_FINISHED | STATE_GESTURE_COMPLETED
-                        | STATE_GESTURE_STARTED_QUICKSTEP | STATE_ASSIST_DATA_RECEIVED,
-                this::preloadAssistData);
 
         mStateCallback.addCallback(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
         mStateCallback.addCallback(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
@@ -383,14 +340,8 @@
                 | STATE_SCALED_CONTROLLER_LAST_TASK,
                 this::notifyTransitionCancelled);
 
-        mStateCallback.addCallback(QUICK_SCRUB_START_UI_STATE, this::onQuickScrubStartUi);
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START
-                | STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub);
-        mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_CURRENT_TASK_FINISHED
-                | STATE_QUICK_SCRUB_END, this::switchToFinalAppAfterQuickScrub);
-
-        mStateCallback.addCallback(LONG_SWIPE_ENTER_STATE, this::checkLongSwipeCanEnter);
-        mStateCallback.addCallback(LONG_SWIPE_START_STATE, this::checkLongSwipeCanStart);
+        mStateCallback.addCallback(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
+                mRecentsAnimationWrapper::enableInputConsumer);
 
         if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             mStateCallback.addChangeHandler(STATE_APP_CONTROLLER_RECEIVED | STATE_LAUNCHER_PRESENT
@@ -410,9 +361,9 @@
     private void initTransitionEndpoints(DeviceProfile dp) {
         mDp = dp;
 
-        TransformedRect tempRect = new TransformedRect();
+        Rect tempRect = new Rect();
         mTransitionDragLength = mActivityControlHelper.getSwipeUpDestinationAndLength(
-                dp, mContext, mInteractionType, tempRect);
+                dp, mContext, tempRect);
         mClipAnimationHelper.updateTargetRect(tempRect);
     }
 
@@ -441,7 +392,6 @@
             int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
             initStateCallbacks();
             mStateCallback.setState(oldState);
-            mLayoutListener.setHandler(null);
         }
         mWasLauncherAlreadyVisible = alreadyOnHome;
         mActivity = activity;
@@ -454,19 +404,18 @@
         }
 
         mRecentsView = activity.getOverviewPanel();
-        SyncRtSurfaceTransactionApplierCompat.create(mRecentsView, (applier) -> {
-            mSyncTransactionApplier = applier;
-        });
+        SyncRtSurfaceTransactionApplierCompat.create(mRecentsView,
+                applier ->  mSyncTransactionApplier = applier );
         mRecentsView.setEnableFreeScroll(false);
+
         mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
-            if (!mLongSwipeMode && mGestureEndTarget != HOME) {
+            if (mGestureEndTarget != HOME) {
                 updateFinalShift();
             }
         });
         mRecentsView.setRecentsAnimationWrapper(mRecentsAnimationWrapper);
         mRecentsView.setClipAnimationHelper(mClipAnimationHelper);
-        mQuickScrubController = mRecentsView.getQuickScrubController();
-        mLayoutListener = mActivityControlHelper.createLayoutListener(mActivity);
+        mActivity.getRootView().getOverlay().add(mLiveTileOverlay);
 
         mStateCallback.setState(STATE_LAUNCHER_PRESENT);
         if (alreadyOnHome) {
@@ -512,7 +461,7 @@
         }
 
         setupRecentsViewUi();
-        mLayoutListener.open();
+        activity.getRootView().setOnApplyWindowInsetsListener(this);
         mStateCallback.setState(STATE_LAUNCHER_STARTED);
     }
 
@@ -554,45 +503,18 @@
     }
 
     private void initializeLauncherAnimationController() {
-        mLayoutListener.setHandler(this);
         buildAnimationController();
 
         if (LatencyTrackerCompat.isEnabled(mContext)) {
             LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
         }
 
-        // This method is only called when STATE_GESTURE_STARTED_QUICKSTEP/
-        // STATE_GESTURE_STARTED_QUICKSCRUB is set, so we can enable the high-res thumbnail loader
-        // here once we are sure that we will end up in an overview state
+        // This method is only called when STATE_GESTURE_STARTED is set, so we can enable the
+        // high-res thumbnail loader here once we are sure that we will end up in an overview state
         RecentsModel.INSTANCE.get(mContext).getThumbnailCache()
                 .getHighResLoadingState().setVisible(true);
     }
 
-    private void shiftAnimationDestinationForQuickscrub() {
-        TransformedRect tempRect = new TransformedRect();
-        mActivityControlHelper
-                .getSwipeUpDestinationAndLength(mDp, mContext, mInteractionType, tempRect);
-        mClipAnimationHelper.updateTargetRect(tempRect);
-
-        float offsetY =
-                mActivityControlHelper.getTranslationYForQuickScrub(tempRect, mDp, mContext);
-        float scale, offsetX;
-        Resources res = mContext.getResources();
-
-        if (ActivityManagerWrapper.getInstance().getRecentTasks(2, UserHandle.myUserId()).size()
-                < 2) {
-            // There are not enough tasks, we don't need to shift
-            offsetX = 0;
-            scale = 1;
-        } else {
-            offsetX = res.getDimensionPixelSize(R.dimen.recents_page_spacing)
-                    + tempRect.rect.width();
-            scale = getTaskCurveScaleForOffsetX(offsetX, tempRect.rect.width());
-        }
-        mClipAnimationHelper.offsetTarget(scale, Utilities.isRtl(res) ? -offsetX : offsetX, offsetY,
-                QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
-    }
-
     private float getTaskCurveScaleForOffsetX(float offsetX, float taskWidth) {
         float distanceToReachEdge = mDp.widthPx / 2 + taskWidth / 2 +
                 mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
@@ -600,24 +522,8 @@
         return TaskView.getCurveScaleForInterpolation(interpolation);
     }
 
-    @UiThread
-    public void dispatchMotionEventToRecentsView(MotionEvent event, @Nullable Float velocityX) {
-        if (mRecentsView == null) {
-            return;
-        }
-        // Pass the motion events to RecentsView to allow scrolling during swipe up.
-        if (!mDispatchedDownEvent) {
-            // The first event we dispatch should be ACTION_DOWN.
-            mDispatchedDownEvent = true;
-            MotionEvent downEvent = MotionEvent.obtain(event);
-            downEvent.setAction(MotionEvent.ACTION_DOWN);
-            int flags = downEvent.getEdgeFlags();
-            downEvent.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
-            mRecentsView.simulateTouchEvent(downEvent, velocityX);
-            downEvent.recycle();
-        }
-
-        mRecentsView.simulateTouchEvent(event, velocityX);
+    public Consumer<MotionEvent> getRecentsViewDispatcher() {
+        return mRecentsView != null ? mRecentsView::dispatchTouchEvent : null;
     }
 
     @UiThread
@@ -626,16 +532,7 @@
         displacement = -displacement;
         if (displacement > mTransitionDragLength && mTransitionDragLength > 0) {
             mCurrentShift.updateValue(1);
-
-            if (!mLongSwipeMode && !FeatureFlags.SWIPE_HOME.get()) {
-                onLongSwipeEnabled();
-            }
-            mLongSwipeDisplacement = displacement - mTransitionDragLength;
-            onLongSwipeDisplacementUpdated();
         } else {
-            if (mLongSwipeMode) {
-                onLongSwipeDisabled();
-            }
             float translation = Math.max(displacement, 0);
             float shift = mTransitionDragLength == 0 ? 0 : translation / mTransitionDragLength;
             mCurrentShift.updateValue(shift);
@@ -648,22 +545,24 @@
 
     @UiThread
     public void setShelfState(ShelfAnimState shelfState, Interpolator interpolator, long duration) {
-        if (mInteractionType == INTERACTION_NORMAL) {
-            mAnimationFactory.setShelfState(shelfState, interpolator, duration);
-            mIsShelfPeeking = shelfState == PEEK;
-            if (mRecentsView != null && shelfState.shouldPreformHaptic) {
-                mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-            }
+        mAnimationFactory.setShelfState(shelfState, interpolator, duration);
+        mIsShelfPeeking = shelfState == PEEK;
+        if (mRecentsView != null && shelfState.shouldPreformHaptic) {
+            mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
         }
     }
 
-    /**
-     * Called by {@link #mLayoutListener} when launcher layout changes
-     */
-    public void buildAnimationController() {
+    private void buildAnimationController() {
         initTransitionEndpoints(mActivity.getDeviceProfile());
-        mAnimationFactory.createActivityController(mTransitionDragLength, mInteractionType);
+        mAnimationFactory.createActivityController(mTransitionDragLength);
+    }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
+        WindowInsets result = view.onApplyWindowInsets(windowInsets);
+        buildAnimationController();
+        return result;
     }
 
     private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) {
@@ -676,10 +575,10 @@
     private void updateFinalShift() {
         float shift = mCurrentShift.value;
 
-        RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
+        SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
         if (controller != null) {
             float offsetX = 0;
-            if (mRecentsView != null && mInteractionType == INTERACTION_NORMAL) {
+            if (mRecentsView != null) {
                 int startScroll = mRecentsView.getScrollForPage(mRecentsView.indexOfChild(
                         mRecentsView.getRunningTaskView()));
                 offsetX = startScroll - mRecentsView.getScrollX();
@@ -691,19 +590,13 @@
                     .setSyncTransactionApplier(mSyncTransactionApplier);
             mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet,
                     mTransformParams);
-
-            boolean passedThreshold = shift > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD;
-            mRecentsAnimationWrapper.setAnimationTargetsBehindSystemBars(!passedThreshold);
-            if (mActivityControlHelper.shouldMinimizeSplitScreen()) {
-                mRecentsAnimationWrapper.setSplitScreenMinimizedForTransaction(passedThreshold);
-            }
+            mRecentsAnimationWrapper.setWindowThresholdCrossed(
+                    shift > 1 - UPDATE_SYSUI_FLAGS_THRESHOLD);
         }
 
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsAnimationWrapper.getController() != null && mLayoutListener != null) {
-                mLayoutListener.open();
-                mLayoutListener.update(mCurrentShift.value > 1, mLongSwipeMode,
-                        mClipAnimationHelper.getCurrentRectWithInsets(),
+            if (mRecentsAnimationWrapper.getController() != null) {
+                mLiveTileOverlay.update(mClipAnimationHelper.getCurrentRectWithInsets(),
                         mClipAnimationHelper.getCurrentCornerRadius());
             }
         }
@@ -711,15 +604,14 @@
         final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
         if (passed != mPassedOverviewThreshold) {
             mPassedOverviewThreshold = passed;
-            if (mInteractionType == INTERACTION_NORMAL && mRecentsView != null
-                    && !SWIPE_HOME.get()) {
+            if (mRecentsView != null && !SWIPE_HOME.get()) {
                 mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
                     HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
             }
         }
         // Update insets of the non-running tasks, as we might switch to them.
         int runningTaskIndex = mRecentsView == null ? -1 : mRecentsView.getRunningTaskIndex();
-        if (mInteractionType == INTERACTION_NORMAL && runningTaskIndex >= 0) {
+        if (runningTaskIndex >= 0) {
             for (int i = 0; i < mRecentsView.getTaskViewCount(); i++) {
                 if (i != runningTaskIndex) {
                     mRecentsView.getTaskViewAt(i).setFullscreenProgress(1 - mCurrentShift.value);
@@ -778,8 +670,8 @@
         mClipAnimationHelper.prepareAnimation(false /* isOpening */);
         initTransitionEndpoints(dp);
 
-        mRecentsAnimationWrapper.setController(targetSet.controller, targetSet);
-        mTouchInteractionLog.startRecentsAnimationCallback(targetSet.apps.length);
+        mRecentsAnimationWrapper.setController(targetSet);
+        TOUCH_INTERACTION_LOG.startRecentsAnimationCallback(targetSet.apps.length);
         setStateOnUiThread(STATE_APP_CONTROLLER_RECEIVED);
 
         mPassedOverviewThreshold = false;
@@ -787,21 +679,18 @@
 
     @Override
     public void onRecentsAnimationCanceled() {
-        mRecentsAnimationWrapper.setController(null, null);
+        mRecentsAnimationWrapper.setController(null);
         mActivityInitListener.unregister();
         setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
-        mTouchInteractionLog.cancelRecentsAnimation();
+        TOUCH_INTERACTION_LOG.cancelRecentsAnimation();
     }
 
     @UiThread
     public void onGestureStarted() {
         notifyGestureStartedAsync();
         mShiftAtGestureStart = mCurrentShift.value;
-        setStateOnUiThread(mInteractionType == INTERACTION_NORMAL
-                ? STATE_GESTURE_STARTED_QUICKSTEP : STATE_GESTURE_STARTED_QUICKSCRUB);
+        setStateOnUiThread(STATE_GESTURE_STARTED);
         mGestureStarted = true;
-        mRecentsAnimationWrapper.hideCurrentInputMethod();
-        mRecentsAnimationWrapper.enableInputConsumer();
     }
 
     /**
@@ -825,16 +714,11 @@
         setStateOnUiThread(STATE_GESTURE_COMPLETED);
 
         mLogAction = isFling ? Touch.FLING : Touch.SWIPE;
-
-        if (mLongSwipeMode) {
-            onLongSwipeGestureFinish(endVelocity, isFling, velocityX);
-        } else {
-            handleNormalGestureEnd(endVelocity, isFling, velocityX);
-        }
+        handleNormalGestureEnd(endVelocity, isFling, velocityX);
     }
 
     @UiThread
-    private TouchConsumer createNewTouchProxyHandler() {
+    private InputConsumer createNewInputProxyHandler() {
         mCurrentShift.finishAnimation();
         if (mLauncherTransitionController != null) {
             mLauncherTransitionController.getAnimationPlayer().end();
@@ -844,8 +728,7 @@
             setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
         }
 
-        return OverviewTouchConsumer.newInstance(mActivityControlHelper, true,
-                mTouchInteractionLog);
+        return OverviewInputConsumer.newInstance(mActivityControlHelper, true);
     }
 
     @UiThread
@@ -939,7 +822,7 @@
             setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
             duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
         } else if (endTarget == RECENTS) {
-            mRecentsAnimationWrapper.enableTouchProxy();
+            mRecentsAnimationWrapper.enableInputProxy();
             if (mRecentsView != null) {
                 duration = Math.max(duration, mRecentsView.getScroller().getDuration());
             }
@@ -989,13 +872,24 @@
     private void animateToProgressInternal(float start, float end, long duration,
             Interpolator interpolator, GestureEndTarget target, float velocityPxPerMs) {
         mGestureEndTarget = target;
-        ActivityControlHelper.HomeAnimationFactory homeAnimFactory;
+
+        if (mGestureEndTarget.canBeContinued) {
+            // Because we might continue this gesture, e.g. for consecutive quick switch, we need to
+            // stabilize the task list so that tasks don't rearrange in the middle of the gesture.
+            RecentsModel.INSTANCE.get(mContext).startStabilizationSession();
+        } else if (mGestureEndTarget.isLauncher) {
+            // Otherwise, if we're going to home or overview,
+            // we reset the tasks to a consistent start state.
+            RecentsModel.INSTANCE.get(mContext).endStabilizationSession();
+        }
+
+        HomeAnimationFactory homeAnimFactory;
         Animator windowAnim;
         if (mGestureEndTarget == HOME) {
             if (mActivity != null) {
                 homeAnimFactory = mActivityControlHelper.prepareHomeUI(mActivity);
             } else {
-                homeAnimFactory = new ActivityControlHelper.HomeAnimationFactory() {
+                homeAnimFactory = new HomeAnimationFactory() {
                     @NonNull
                     @Override
                     public RectF getWindowTargetRect() {
@@ -1013,7 +907,7 @@
                 mStateCallback.addChangeHandler(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
                         isPresent -> mRecentsView.startHome());
             }
-            windowAnim = createWindowAnimationToHome(start, homeAnimFactory.getWindowTargetRect());
+            windowAnim = createWindowAnimationToHome(start, homeAnimFactory);
             mLauncherTransitionController = null;
         } else {
             windowAnim = mCurrentShift.animateToValue(start, end);
@@ -1063,20 +957,25 @@
     /**
      * Creates an Animator that transforms the current app window into the home app.
      * @param startProgress The progress of {@link #mCurrentShift} to start the window from.
-     * @param endTarget Where to animate the window towards.
+     * @param homeAnimationFactory The home animation factory.
      */
-    private Animator createWindowAnimationToHome(float startProgress, RectF endTarget) {
+    private Animator createWindowAnimationToHome(float startProgress,
+            HomeAnimationFactory homeAnimationFactory) {
         final RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
         RectF startRect = new RectF(mClipAnimationHelper.applyTransform(targetSet,
                 mTransformParams.setProgress(startProgress)));
         RectF originalTarget = new RectF(mClipAnimationHelper.getTargetRect());
-        final RectF finalTarget = endTarget;
+        final RectF finalTarget = homeAnimationFactory.getWindowTargetRect();
 
         final RectFEvaluator rectFEvaluator = new RectFEvaluator();
         final RectF targetRect = new RectF();
         final RectF currentRect = new RectF();
 
+        final View floatingView = homeAnimationFactory.getFloatingView();
         ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+        if (floatingView instanceof FloatingIconView) {
+            anim.addListener((FloatingIconView) floatingView);
+        }
         anim.addUpdateListener(animation -> {
             float progress = animation.getAnimatedFraction();
             float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
@@ -1091,6 +990,10 @@
             mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
                     .setSyncTransactionApplier(mSyncTransactionApplier);
             mClipAnimationHelper.applyTransform(targetSet, mTransformParams);
+
+            if (floatingView instanceof FloatingIconView) {
+                ((FloatingIconView) floatingView).update(currentRect, 1f - alpha);
+            }
         });
         anim.addListener(new AnimationSuccessListener() {
             @Override
@@ -1113,7 +1016,7 @@
     @UiThread
     private void resumeLastTask() {
         mRecentsAnimationWrapper.finish(false /* toRecents */, null);
-        mTouchInteractionLog.finishRecentsAnimation(false);
+        TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
     }
 
     @UiThread
@@ -1131,16 +1034,12 @@
                         mMainThreadHandler);
             });
         }
-        mTouchInteractionLog.finishRecentsAnimation(false);
+        TOUCH_INTERACTION_LOG.finishRecentsAnimation(false);
         doLogGesture(NEW_TASK);
     }
 
     public void reset() {
-        if (mInteractionType != INTERACTION_QUICK_SCRUB) {
-            // Only invalidate the handler if we are not quick scrubbing, otherwise, it will be
-            // invalidated after the quick scrub ends
-            setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-        }
+        setStateOnUiThread(STATE_HANDLER_INVALIDATED);
     }
 
     public void cancel() {
@@ -1164,13 +1063,16 @@
 
     private void invalidateHandlerWithLauncher() {
         mLauncherTransitionController = null;
-        mLayoutListener.finish();
         mActivityControlHelper.getAlphaProperty(mActivity).setValue(1);
 
         mRecentsView.setEnableFreeScroll(true);
         mRecentsView.setRunningTaskIconScaledDown(false);
         mRecentsView.setOnScrollChangeListener(null);
-        mQuickScrubController.cancelActiveQuickscrub();
+        mRecentsView.setRunningTaskHidden(false);
+        mRecentsView.setEnableDrawingLiveTile(true);
+
+        mActivity.getRootView().setOnApplyWindowInsetsListener(null);
+        mActivity.getRootView().getOverlay().remove(mLiveTileOverlay);
     }
 
     private void notifyTransitionCancelled() {
@@ -1185,20 +1087,12 @@
         mActivity.clearForceInvisibleFlag(INVISIBLE_BY_STATE_HANDLER);
     }
 
-    public void layoutListenerClosed() {
-        mRecentsView.setRunningTaskHidden(false);
-        if (mWasLauncherAlreadyVisible && mLauncherTransitionController != null) {
-            mLauncherTransitionController.setPlayFraction(1);
-        }
-        mRecentsView.setEnableDrawingLiveTile(true);
-    }
-
     private void switchToScreenshot() {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             setStateOnUiThread(STATE_SCREENSHOT_CAPTURED);
         } else {
             boolean finishTransitionPosted = false;
-            RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
+            SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
             if (controller != null) {
                 // Update the screenshot of the task
                 if (mTaskSnapshot == null) {
@@ -1249,7 +1143,7 @@
                         () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
             }
         }
-        mTouchInteractionLog.finishRecentsAnimation(true);
+        TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
     }
 
     private void finishCurrentTransitionToHome() {
@@ -1257,7 +1151,7 @@
             mRecentsAnimationWrapper.finish(true /* toRecents */,
                     () -> setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
         }
-        mTouchInteractionLog.finishRecentsAnimation(true);
+        TOUCH_INTERACTION_LOG.finishRecentsAnimation(true);
         doLogGesture(HOME);
     }
 
@@ -1278,183 +1172,16 @@
         reset();
     }
 
-    public void onQuickScrubStart() {
-        if (mInteractionType != INTERACTION_NORMAL) {
-            throw new IllegalArgumentException(
-                    "Can't change interaction type from " + mInteractionType);
-        }
-        mInteractionType = INTERACTION_QUICK_SCRUB;
-        mRecentsAnimationWrapper.runOnInit(this::shiftAnimationDestinationForQuickscrub);
-
-        setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
-
-        // Start the window animation without waiting for launcher.
-        long duration = FeatureFlags.QUICK_SWITCH.get()
-                ? QUICK_SWITCH_FROM_APP_START_DURATION
-                : QUICK_SCRUB_FROM_APP_START_DURATION;
-        animateToProgress(mCurrentShift.value, 1f, duration, LINEAR, RECENTS, 1f);
-    }
-
-    private void onQuickScrubStartUi() {
-        if (!mQuickScrubController.prepareQuickScrub(TAG, FeatureFlags.QUICK_SWITCH.get())) {
-            mQuickScrubBlocked = true;
-            setStateOnUiThread(STATE_RESUME_LAST_TASK | STATE_HANDLER_INVALIDATED);
-            return;
-        }
-        if (mLauncherTransitionController != null) {
-            mLauncherTransitionController.getAnimationPlayer().end();
-            mLauncherTransitionController = null;
-        }
-
-        mActivityControlHelper.onQuickInteractionStart(mActivity, mRunningTaskInfo, false,
-                mTouchInteractionLog);
-
-        // Inform the last progress in case we skipped before.
-        mQuickScrubController.onQuickScrubProgress(mCurrentQuickScrubProgress);
-    }
-
-    private void onFinishedTransitionToQuickScrub() {
-        if (mQuickScrubBlocked) {
-            return;
-        }
-        mLayoutListener.finish();
-        mQuickScrubController.onFinishedTransitionToQuickScrub();
-
-        mRecentsView.animateUpRunningTaskIconScale();
-        if (mQuickScrubController.isQuickSwitch()) {
-            // Adjust the running task so that it is centered and fills the screen.
-            TaskView runningTask = mRecentsView.getRunningTaskView();
-            if (runningTask != null) {
-                float insetHeight = mDp.heightPx - mDp.getInsets().top - mDp.getInsets().bottom;
-                // Usually insetDiff will be 0, unless we allow apps to draw under the insets. In
-                // that case (insetDiff != 0), we need to center in the system-specified available
-                // height rather than launcher's inset height by adding half the insetDiff.
-                float insetDiff = mDp.availableHeightPx - insetHeight;
-                float topMargin = mActivity.getResources().getDimension(
-                        R.dimen.task_thumbnail_half_top_margin);
-                runningTask.setTranslationY((insetDiff / 2 - topMargin) / mRecentsView.getScaleX());
-            }
-        }
-        RecentsModel.INSTANCE.get(mContext).onOverviewShown(false, TAG);
-    }
-
-    public void onQuickScrubProgress(float progress) {
-        mCurrentQuickScrubProgress = progress;
-        if (mQuickScrubController == null || mQuickScrubBlocked ||
-                !mStateCallback.hasStates(QUICK_SCRUB_START_UI_STATE)) {
-            return;
-        }
-        mQuickScrubController.onQuickScrubProgress(progress);
-    }
-
-    public void onQuickScrubEnd() {
-        setStateOnUiThread(STATE_QUICK_SCRUB_END);
-    }
-
-    private void switchToFinalAppAfterQuickScrub() {
-        if (mQuickScrubBlocked) {
-            return;
-        }
-        mQuickScrubController.onQuickScrubEnd();
-
-        // Normally this is handled in reset(), but since we are still scrubbing after the
-        // transition into recents, we need to defer the handler invalidation for quick scrub until
-        // after the gesture ends
-        setStateOnUiThread(STATE_HANDLER_INVALIDATED);
-    }
-
     public void setGestureEndCallback(Runnable gestureEndCallback) {
         mGestureEndCallback = gestureEndCallback;
     }
 
-    // Handling long swipe
-    private void onLongSwipeEnabled() {
-        mLongSwipeMode = true;
-        checkLongSwipeCanEnter();
-        checkLongSwipeCanStart();
-    }
-
-    private void onLongSwipeDisabled() {
-        mLongSwipeMode = false;
-        mStateCallback.clearState(STATE_SCREENSHOT_VIEW_SHOWN);
-
-        if (mLongSwipeController != null) {
-            mLongSwipeController.destroy();
-            setTargetAlphaProvider((t, a1) -> a1);
-
-            // Rebuild animations
-            buildAnimationController();
-        }
-    }
-
-    private void onLongSwipeDisplacementUpdated() {
-        if (!mLongSwipeMode || mLongSwipeController == null) {
-            return;
-        }
-
-        mLongSwipeController.onMove(mLongSwipeDisplacement);
-    }
-
-    private void checkLongSwipeCanEnter() {
-        if (!mLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_ENTER_STATE)
-                || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
-            return;
-        }
-
-        // We are entering long swipe mode, make sure the screen shot is captured.
-        mStateCallback.setState(STATE_CAPTURE_SCREENSHOT | STATE_SCREENSHOT_VIEW_SHOWN);
-
-    }
-
-    private void checkLongSwipeCanStart() {
-        if (!mLongSwipeMode || !mStateCallback.hasStates(LONG_SWIPE_START_STATE)
-                || !mActivityControlHelper.supportsLongSwipe(mActivity)) {
-            return;
-        }
-
-        RemoteAnimationTargetSet targetSet = mRecentsAnimationWrapper.targetSet;
-        if (targetSet == null) {
-            // This can happen when cancelAnimation comes on the background thread, while we are
-            // processing the long swipe on the UI thread.
-            return;
-        }
-
-        mLongSwipeController = mActivityControlHelper.getLongSwipeController(
-                mActivity, mRunningTaskId);
-        onLongSwipeDisplacementUpdated();
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
-        }
-    }
-
-    private void onLongSwipeGestureFinish(float velocity, boolean isFling, float velocityX) {
-        if (!mLongSwipeMode || mLongSwipeController == null) {
-            mLongSwipeMode = false;
-            handleNormalGestureEnd(velocity, isFling, velocityX);
-            return;
-        }
-        mLongSwipeMode = false;
-        finishCurrentTransitionToRecents();
-        mLongSwipeController.end(velocity, isFling,
-                () -> setStateOnUiThread(STATE_HANDLER_INVALIDATED));
-
-    }
-
     private void setTargetAlphaProvider(
             BiFunction<RemoteAnimationTargetCompat, Float, Float> provider) {
         mClipAnimationHelper.setTaskAlphaCallback(provider);
         updateFinalShift();
     }
 
-    public void onAssistDataReceived(Bundle assistData) {
-        mAssistData = assistData;
-        setStateOnUiThread(STATE_ASSIST_DATA_RECEIVED);
-    }
-
-    private void preloadAssistData() {
-        RecentsModel.INSTANCE.get(mContext).preloadAssistData(mRunningTaskId, mAssistData);
-    }
-
     public static float getHiddenTargetAlpha(RemoteAnimationTargetCompat app, Float expectedAlpha) {
         if (!(app.isNotInRecents
                 || app.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME)) {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
similarity index 96%
rename from quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
index 9679b81..e254e24 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -35,7 +35,6 @@
     public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setOverviewStateEnabled(true);
-        getQuickScrubController().onFinishedTransitionToQuickScrub();
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
similarity index 89%
rename from quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
index ca8c252..6b3f028 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsRootView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -30,13 +30,14 @@
 
 public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
 
+    private static final int MIN_SIZE = 10;
     private final RecentsActivity mActivity;
 
-    private final Point mLastKnownSize = new Point(10, 10);
+    private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
 
     public RecentsRootView(Context context, AttributeSet attrs) {
         super(context, attrs, 1 /* alphaChannelCount */);
-        mActivity = (RecentsActivity) BaseActivity.fromContext(context);
+        mActivity = BaseActivity.fromContext(context);
         setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | SYSTEM_UI_FLAG_LAYOUT_STABLE);
@@ -53,8 +54,8 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Check size changes before the actual measure, to avoid multiple measure calls.
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
+        int width = Math.max(MIN_SIZE, MeasureSpec.getSize(widthMeasureSpec));
+        int height = Math.max(MIN_SIZE, MeasureSpec.getSize(heightMeasureSpec));
         if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
             mLastKnownSize.set(width, height);
             mActivity.onRootViewSizeChanged();
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/fallback/RecentsTaskController.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintUtil.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintUtil.java
new file mode 100644
index 0000000..f2d40ec
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintUtil.java
@@ -0,0 +1,78 @@
+/*
+ * 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.hints;
+
+import android.app.PendingIntent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+
+public final class HintUtil {
+
+    public static final String ID_KEY = "id";
+    public static final String ICON_KEY = "icon";
+    public static final String TEXT_KEY = "text";
+    public static final String TAP_ACTION_KEY = "tap_action";
+
+    private HintUtil() {}
+
+    public static Bundle makeHint(String id, Icon icon, CharSequence text) {
+        Bundle hint = new Bundle();
+        hint.putString(ID_KEY, id);
+        hint.putParcelable(ICON_KEY, icon);
+        hint.putCharSequence(TEXT_KEY, text);
+        return hint;
+    }
+
+    public static Bundle makeHint(Icon icon, CharSequence text, PendingIntent tapAction) {
+        Bundle hint = new Bundle();
+        hint.putParcelable(ICON_KEY, icon);
+        hint.putCharSequence(TEXT_KEY, text);
+        hint.putParcelable(TAP_ACTION_KEY, tapAction);
+        return hint;
+    }
+
+    public static String getId(Bundle hint) {
+        String id = hint.getString(ID_KEY);
+        if (id == null) {
+            throw new IllegalArgumentException("Hint does not contain an ID");
+        }
+        return id;
+    }
+
+    public static Icon getIcon(Bundle hint) {
+        Icon icon = hint.getParcelable(ICON_KEY);
+        if (icon == null) {
+            throw new IllegalArgumentException("Hint does not contain an icon");
+        }
+        return icon;
+    }
+
+    public static CharSequence getText(Bundle hint) {
+        CharSequence text = hint.getCharSequence(TEXT_KEY);
+        if (text == null) {
+            throw new IllegalArgumentException("Hint does not contain text");
+        }
+        return text;
+    }
+
+    public static PendingIntent getTapAction(Bundle hint) {
+        PendingIntent tapAction = hint.getParcelable(TAP_ACTION_KEY);
+        if (tapAction == null) {
+            throw new IllegalArgumentException("Hint does not contain a tap action");
+        }
+        return tapAction;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintView.java
new file mode 100644
index 0000000..5399cc4
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintView.java
@@ -0,0 +1,71 @@
+/*
+ * 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.hints;
+
+import static com.android.quickstep.hints.HintUtil.getIcon;
+import static com.android.quickstep.hints.HintUtil.getText;
+
+import android.content.Context;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+public class HintView extends LinearLayout {
+    private ImageView mIconView;
+    private TextView mLabelView;
+
+    public HintView(Context context) {
+        super(context);
+    }
+
+    public HintView(Context context, @Nullable AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HintView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public HintView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    public void setHint(Bundle hint) {
+        mLabelView.setText(getText(hint));
+
+        Icon icon = getIcon(hint);
+        if (icon == null) {
+            mIconView.setVisibility(GONE);
+        } else {
+            mIconView.setImageIcon(icon);
+            mIconView.setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mIconView = findViewById(R.id.icon);
+        mLabelView = findViewById(R.id.label);
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintsContainer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintsContainer.java
new file mode 100644
index 0000000..22b1217
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/HintsContainer.java
@@ -0,0 +1,218 @@
+/*
+ * 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.hints;
+
+import static com.android.quickstep.hints.UiHintListenerConstants.HINTS_KEY;
+import static com.android.quickstep.hints.UiHintListenerConstants.ON_HINTS_RETURNED_CODE;
+import static com.android.quickstep.hints.UiInterfaceConstants.REQUEST_HINTS_CODE;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.widget.LinearLayout;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+
+public class HintsContainer extends LinearLayout {
+
+    private static final String TAG = "HintsView";
+
+    public static final FloatProperty<HintsContainer> HINT_VISIBILITY =
+            new FloatProperty<HintsContainer>("hint_visibility") {
+                @Override
+                public void setValue(HintsContainer hintsContainer, float v) {
+                    hintsContainer.setHintVisibility(v);
+                }
+
+                @Override
+                public Float get(HintsContainer hintsContainer) {
+                    return hintsContainer.mHintVisibility;
+                }
+            };
+
+    private static Intent mServiceIntent =
+            new Intent("com.android.systemui.action.UI_PULL_INTERFACE")
+                    .setClassName(
+                            "com.android.systemui.navbarhint",
+                            "com.android.systemui.navbarhint.service.HintService");
+
+    @Nullable
+    private Messenger mHintServiceInterface;
+    private UiHintListener mUiHintListener;
+    private boolean mBound = false;
+    private float mHintVisibility;
+
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
+            mHintServiceInterface = new Messenger(iBinder);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mHintServiceInterface = null;
+            attemptBinding();
+        }
+
+        @Override
+        public void onBindingDied(ComponentName componentName) {
+            mHintServiceInterface = null;
+            attemptBinding();
+        }
+    };
+
+    public HintsContainer(Context context) {
+        super(context);
+    }
+
+    public HintsContainer(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public HintsContainer(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mUiHintListener == null) {
+            mUiHintListener = new UiHintListener(this);
+        }
+        if (!mBound) {
+            attemptBinding();
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        if (mBound) {
+            getContext().unbindService(mServiceConnection);
+            mBound = false;
+        }
+        super.onDetachedFromWindow();
+    }
+
+    public void setHintVisibility(float v) {
+        if (v == 1) {
+            getHints();
+            setVisibility(VISIBLE);
+        } else {
+            setVisibility(GONE);
+        }
+        mHintVisibility = v;
+    }
+
+    private void attemptBinding() {
+        if (mBound) {
+            getContext().unbindService(mServiceConnection);
+            mBound = false;
+        }
+        boolean success = getContext().bindService(mServiceIntent,
+                mServiceConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT);
+        if (success) {
+            mBound = true;
+        } else {
+            Log.w(TAG, "Binding to hint supplier failed");
+        }
+    }
+    
+    private void sendOnHintTap(Bundle hint) {
+        if (mHintServiceInterface != null) {
+            Message msg = Message.obtain(null, UiInterfaceConstants.ON_HINT_TAP_CODE);
+            Bundle data = new Bundle();
+            data.putString(UiInterfaceConstants.HINT_ID_KEY, HintUtil.getId(hint));
+            data.putInt(UiInterfaceConstants.WIDTH_PX_KEY, getWidth());
+            data.putInt(UiInterfaceConstants.HEIGHT_PX_KEY, getHeight());
+            data.putInt(UiInterfaceConstants.HINT_SPACE_WIDTH_PX_KEY, 0);
+            data.putInt(UiInterfaceConstants.HINT_SPACE_HEIGHT_PX_KEY, 0);
+            msg.setData(data);
+            try {
+                mHintServiceInterface.send(msg);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send hint tap", e);
+            }
+        }
+    }
+
+    private void getHints() {
+        if (mHintServiceInterface != null) {
+            try {
+                Message m = Message.obtain(null, REQUEST_HINTS_CODE);
+                m.replyTo = new Messenger(mUiHintListener);
+                mHintServiceInterface.send(m);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send message", e);
+            }
+        }
+    }
+
+    private static class UiHintListener extends Handler {
+        private HintsContainer mView;
+
+        UiHintListener(HintsContainer v) {
+            mView = v;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case ON_HINTS_RETURNED_CODE:
+                    handleHints(msg);
+                    break;
+                default:
+                    Log.e(TAG, "UiPullInterface got unrecognized code: " + msg.what);
+                    break;
+            }
+        }
+
+        private void handleHints(Message msg) {
+            Bundle bundle = msg.getData();
+            ArrayList<Bundle> hints = bundle.getParcelableArrayList(HINTS_KEY);
+
+            if (hints != null) {
+                mView.removeAllViews();
+
+                for (Bundle hint : hints) {
+                    HintView h = (HintView) LayoutInflater.from(mView.getContext()).inflate(
+                            R.layout.hint, mView, false);
+                    h.setHint(hint);
+                    h.setOnClickListener((v) -> mView.sendOnHintTap(hint));
+                    mView.addView(h);
+                }
+            }
+        }
+    }
+}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/UiHintListenerConstants.java
similarity index 65%
rename from go/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/hints/UiHintListenerConstants.java
index 90360ce..420033d 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/UiHintListenerConstants.java
@@ -13,14 +13,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.quickstep.hints;
 
-/**
- * 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);
-    }
+public final class UiHintListenerConstants {
+
+    private UiHintListenerConstants() {}
+
+    // Operations
+    public static final int ON_HINTS_RETURNED_CODE = 5;
+
+    // Keys
+    public static final String SESSION_ID_KEY = "session_id";
+    public static final String HINTS_KEY = "hints";
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/UiInterfaceConstants.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/UiInterfaceConstants.java
new file mode 100644
index 0000000..0140613
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/hints/UiInterfaceConstants.java
@@ -0,0 +1,34 @@
+/*
+ * 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.hints;
+
+public final class UiInterfaceConstants {
+
+    private UiInterfaceConstants() {}
+
+    // Operations
+    public static final int ON_HINT_TAP_CODE = 4;
+
+    public static final int REQUEST_HINTS_CODE = 7;
+
+    // Keys
+    public static final String SESSION_ID_KEY = "session_id";
+    public static final String HINT_ID_KEY = "hint_id";
+    public static final String WIDTH_PX_KEY = "width_px";
+    public static final String HEIGHT_PX_KEY = "height_px";
+    public static final String HINT_SPACE_WIDTH_PX_KEY = "hint_space_width_px";
+    public static final String HINT_SPACE_HEIGHT_PX_KEY = "hint_space_height_px";
+}
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
similarity index 89%
rename from quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
index 720c1c4..4450b4b 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -15,9 +15,7 @@
  */
 package com.android.quickstep.util;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
@@ -26,19 +24,15 @@
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.graphics.Matrix.ScaleToFit;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
 import android.os.RemoteException;
-import android.view.animation.Interpolator;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.views.RecentsView;
@@ -53,6 +47,8 @@
 
 import java.util.function.BiFunction;
 
+import androidx.annotation.Nullable;
+
 /**
  * Utility class to handle window clip animation
  */
@@ -67,8 +63,6 @@
     private final RectF mSourceRect = new RectF();
     // The bounds of the task view in launcher window coordinates
     private final RectF mTargetRect = new RectF();
-    // Set when the final window destination is changed, such as offsetting for quick scrub
-    private final PointF mTargetOffset = new PointF();
     // The insets to be used for clipping the app window, which can be larger than mSourceInsets
     // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
     // app window coordinates.
@@ -95,11 +89,6 @@
 
     // Corner radius currently applied to transformed window.
     private float mCurrentCornerRadius;
-    private float mTargetScale = 1f;
-    private float mOffsetScale = 1f;
-    private Interpolator mInterpolator = LINEAR;
-    // We translate y slightly faster than the rest of the animation for quick scrub.
-    private Interpolator mOffsetYInterpolator = LINEAR;
 
     // Whether to boost the opening animation target layers, or the closing
     private int mBoostModeTargetLayers = -1;
@@ -130,13 +119,11 @@
         updateSourceStack(target);
     }
 
-    public void updateTargetRect(TransformedRect targetRect) {
-        mOffsetScale = targetRect.scale;
+    public void updateTargetRect(Rect targetRect) {
         mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
                 mSourceStackBounds.width() - mSourceInsets.right,
                 mSourceStackBounds.height() - mSourceInsets.bottom);
-        mTargetRect.set(targetRect.rect);
-        Utilities.scaleRectFAboutCenter(mTargetRect, targetRect.scale);
+        mTargetRect.set(targetRect);
         mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
                 mHomeStackBounds.top - mSourceStackBounds.top);
 
@@ -165,18 +152,11 @@
         if (params.currentRect == null) {
             RectF currentRect;
             mTmpRectF.set(mTargetRect);
-            Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale * params.offsetScale);
-            float offsetYProgress = mOffsetYInterpolator.getInterpolation(params.progress);
-            float progress = mInterpolator.getInterpolation(params.progress);
+            Utilities.scaleRectFAboutCenter(mTmpRectF, params.offsetScale);
+            float progress = params.progress;
             currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
             currentRect.offset(params.offsetX, 0);
 
-            synchronized (mTargetOffset) {
-                // Stay lined up with the center of the target, since it moves for quick scrub.
-                currentRect.offset(mTargetOffset.x * mOffsetScale * progress,
-                        mTargetOffset.y  * offsetYProgress);
-            }
-
             final RectF sourceWindowClipInsets = params.forLiveTile
                     ? mSourceWindowClipInsetsForLiveTile : mSourceWindowClipInsets;
             mClipRectF.left = sourceWindowClipInsets.left * progress;
@@ -205,6 +185,7 @@
                     if (mSupportsRoundedCornersOnWindows) {
                         cornerRadius = Utilities.mapRange(params.progress, mWindowCornerRadius,
                                 mTaskCornerRadius);
+                        mCurrentCornerRadius = cornerRadius;
                     }
                 }
                 alpha = mTaskAlphaCallback.apply(app, params.targetAlpha);
@@ -246,16 +227,6 @@
         mTaskAlphaCallback = callback;
     }
 
-    public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
-        synchronized (mTargetOffset) {
-            mTargetOffset.set(offsetX, offsetY);
-        }
-        mTargetScale = scale;
-        mInterpolator = interpolator;
-        mOffsetYInterpolator = Interpolators.clampToProgress(mInterpolator, 0,
-                QUICK_SCRUB_TRANSLATION_Y_FACTOR);
-    }
-
     public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
         fromTaskThumbnailView(ttv, rv, null);
     }
@@ -280,8 +251,8 @@
             mSourceInsets.set(ttv.getInsets(fallback));
         }
 
-        TransformedRect targetRect = new TransformedRect();
-        dl.getDescendantRectRelativeToSelf(ttv, targetRect.rect);
+        Rect targetRect = new Rect();
+        dl.getDescendantRectRelativeToSelf(ttv, targetRect);
         updateTargetRect(targetRect);
 
         if (target == null) {
diff --git a/quickstep/src/com/android/quickstep/util/RecentsAnimationListenerSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
similarity index 83%
rename from quickstep/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
index 686e74d..62f2183 100644
--- a/quickstep/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/RecentsAnimationListenerSet.java
@@ -28,6 +28,7 @@
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.Set;
+import java.util.function.Consumer;
 
 import androidx.annotation.UiThread;
 
@@ -38,6 +39,14 @@
 public class RecentsAnimationListenerSet implements RecentsAnimationListener {
 
     private final Set<SwipeAnimationListener> mListeners = new ArraySet<>();
+    private final boolean mShouldMinimizeSplitScreen;
+    private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
+
+    public RecentsAnimationListenerSet(boolean shouldMinimizeSplitScreen,
+            Consumer<SwipeAnimationTargetSet> onFinishListener) {
+        mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+        mOnFinishListener = onFinishListener;
+    }
 
     @UiThread
     public void addListener(SwipeAnimationListener listener) {
@@ -56,7 +65,8 @@
             RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
             Rect minimizedHomeBounds) {
         SwipeAnimationTargetSet targetSet = new SwipeAnimationTargetSet(controller, targets,
-                homeContentInsets, minimizedHomeBounds);
+                homeContentInsets, minimizedHomeBounds, mShouldMinimizeSplitScreen,
+                mOnFinishListener);
         Utilities.postAsyncCallback(MAIN_THREAD_EXECUTOR.getHandler(), () -> {
             for (SwipeAnimationListener listener : getListeners()) {
                 listener.onRecentsAnimationStart(targetSet);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
new file mode 100644
index 0000000..b682481
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
@@ -0,0 +1,103 @@
+/*
+ * 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.util;
+
+import static com.android.quickstep.TouchInteractionService.BACKGROUND_EXECUTOR;
+import static com.android.quickstep.TouchInteractionService.MAIN_THREAD_EXECUTOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.graphics.Rect;
+
+import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+import java.util.function.Consumer;
+
+/**
+ * Extension of {@link RemoteAnimationTargetSet} with additional information about swipe
+ * up animation
+ */
+public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
+
+    private final boolean mShouldMinimizeSplitScreen;
+    private final Consumer<SwipeAnimationTargetSet> mOnFinishListener;
+
+
+    public final RecentsAnimationControllerCompat controller;
+    public final Rect homeContentInsets;
+    public final Rect minimizedHomeBounds;
+
+    public SwipeAnimationTargetSet(RecentsAnimationControllerCompat controller,
+            RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
+            Rect minimizedHomeBounds, boolean shouldMinimizeSplitScreen,
+            Consumer<SwipeAnimationTargetSet> onFinishListener) {
+        super(targets, MODE_CLOSING);
+        this.controller = controller;
+        this.homeContentInsets = homeContentInsets;
+        this.minimizedHomeBounds = minimizedHomeBounds;
+        this.mShouldMinimizeSplitScreen = shouldMinimizeSplitScreen;
+        this.mOnFinishListener = onFinishListener;
+    }
+
+    public void finishController(boolean toRecents, Runnable callback) {
+        mOnFinishListener.accept(this);
+        BACKGROUND_EXECUTOR.execute(() -> {
+            controller.setInputConsumerEnabled(false);
+            controller.finish(toRecents);
+
+            if (callback != null) {
+                MAIN_THREAD_EXECUTOR.execute(callback);
+            }
+        });
+    }
+
+    public void enableInputConsumer() {
+        BACKGROUND_EXECUTOR.submit(() -> {
+            controller.hideCurrentInputMethod();
+            controller.setInputConsumerEnabled(true);
+        });
+    }
+
+    public void setWindowThresholdCrossed(boolean thresholdCrossed) {
+        BACKGROUND_EXECUTOR.execute(() -> {
+            controller.setAnimationTargetsBehindSystemBars(!thresholdCrossed);
+            if (mShouldMinimizeSplitScreen && thresholdCrossed) {
+                // NOTE: As a workaround for conflicting animations (Launcher animating the task
+                // leash, and SystemUI resizing the docked stack, which resizes the task), we
+                // currently only set the minimized mode, and not the inverse.
+                // TODO: Synchronize the minimize animation with the launcher animation
+                controller.setSplitScreenMinimized(thresholdCrossed);
+            }
+        });
+    }
+
+    public ThumbnailData screenshotTask(int taskId) {
+        return controller != null ? controller.screenshotTask(taskId) : null;
+    }
+
+    public interface SwipeAnimationListener {
+
+        void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet);
+
+        void onRecentsAnimationCanceled();
+    }
+
+    public interface SwipeAnimationFinishListener {
+
+        void onSwipeAnimationFinished(SwipeAnimationTargetSet targetSet);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/TaskViewDrawable.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/util/TaskViewDrawable.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewDrawable.java
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/views/ClearAllButton.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
similarity index 99%
rename from quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
index cbd6b2f..5fe92d5 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/DigitalWellBeingToast.java
@@ -80,6 +80,7 @@
         mTask = task;
 
         if (task.key.userId != UserHandle.myUserId()) {
+            setVisibility(GONE);
             callback.call(1, task.titleDescription);
             return;
         }
diff --git a/quickstep/src/com/android/quickstep/views/IconView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/views/IconView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/IconView.java
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
similarity index 77%
rename from quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 5c8f53c..88fe2ee 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,11 +15,13 @@
  */
 package com.android.quickstep.views;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
+import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
+import static com.android.launcher3.config.FeatureFlags.ENABLE_HINTS_IN_OVERVIEW;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 
 import android.animation.AnimatorSet;
@@ -34,15 +36,15 @@
 import android.view.View;
 import android.view.ViewDebug;
 
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.R;
 import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.util.PendingAnimation;
 import com.android.launcher3.views.ScrimView;
 import com.android.quickstep.OverviewInteractionState;
+import com.android.quickstep.hints.HintsContainer;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.ClipAnimationHelper.TransformParams;
 import com.android.quickstep.util.LayoutUtils;
@@ -75,7 +77,7 @@
     private float mTranslationYFactor;
 
     private final TransformParams mTransformParams = new TransformParams();
-    final LauncherLayoutListener mLauncherLayoutListener;
+    private HintsContainer mHintsContainer;
 
     public LauncherRecentsView(Context context) {
         this(context, null);
@@ -88,7 +90,6 @@
     public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setContentAlpha(0);
-        mLauncherLayoutListener = new LauncherLayoutListener(BaseActivity.fromContext(context));
     }
 
     @Override
@@ -107,11 +108,21 @@
         setTranslationYFactor(mTranslationYFactor);
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mHintsContainer = mActivity.findViewById(R.id.hints);
+        mHintsContainer.setPadding(0, 0, 0, mActivity.getDeviceProfile().chipHintBottomMarginPx);
+    }
+
     public void setTranslationYFactor(float translationFactor) {
         mTranslationYFactor = translationFactor;
         setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            redrawLiveTile(false);
+            LauncherState state = mActivity.getStateManager().getState();
+            if (state == OVERVIEW || state == ALL_APPS) {
+                redrawLiveTile(false);
+            }
         }
     }
 
@@ -119,6 +130,12 @@
         return translationYFactor * (getPaddingBottom() - getPaddingTop());
     }
 
+    public void setHintVisibility(float v) {
+        if (mHintsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
+            mHintsContainer.setHintVisibility(v);
+        }
+    }
+
     @Override
     public void draw(Canvas canvas) {
         maybeDrawEmptyMessage(canvas);
@@ -170,6 +187,37 @@
     }
 
     @Override
+    public PendingAnimation createTaskLauncherAnimation(TaskView tv, long duration) {
+        PendingAnimation anim = super.createTaskLauncherAnimation(tv, duration);
+
+        if (ENABLE_HINTS_IN_OVERVIEW.get()) {
+            anim.anim.play(ObjectAnimator.ofFloat(
+                    mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
+        }
+
+        return anim;
+    }
+
+    @Override
+    public PendingAnimation createTaskDismissAnimation(TaskView taskView, boolean animateTaskView,
+            boolean shouldRemoveTask, long duration) {
+        PendingAnimation anim = super.createTaskDismissAnimation(taskView, animateTaskView,
+                shouldRemoveTask, duration);
+
+        if (ENABLE_HINTS_IN_OVERVIEW.get()) {
+            anim.anim.play(ObjectAnimator.ofFloat(
+                    mHintsContainer, HintsContainer.HINT_VISIBILITY, 0));
+            anim.addEndListener(onEndListener -> {
+                if (!onEndListener.isSuccess) {
+                    mHintsContainer.setHintVisibility(1);
+                }
+            });
+        }
+
+        return anim;
+    }
+
+    @Override
     protected void getTaskSize(DeviceProfile dp, Rect outRect) {
         LayoutUtils.calculateLauncherTaskSize(getContext(), dp, outRect);
     }
@@ -200,12 +248,8 @@
 
     @Override
     public void redrawLiveTile(boolean mightNeedToRefill) {
-        AbstractFloatingView layoutListener = AbstractFloatingView.getTopOpenViewWithType(
-                mActivity, TYPE_QUICKSTEP_PREVIEW);
-        if (layoutListener != null && layoutListener.isOpen()) {
-            return;
-        }
-        if (mRecentsAnimationWrapper == null || mClipAnimationHelper == null) {
+        if (!mEnableDrawingLiveTile || mRecentsAnimationWrapper == null
+                || mClipAnimationHelper == null) {
             return;
         }
         TaskView taskView = getRunningTaskView();
@@ -216,7 +260,11 @@
             int offsetY = (int) (mTaskHeight * taskView.getScaleY() * getScaleY()
                     - mTempRect.height());
             if (((mCurrentPage != 0) || mightNeedToRefill) && offsetX > 0) {
-                mTempRect.right += offsetX;
+                if (mTempRect.left - offsetX < 0) {
+                    mTempRect.left -= offsetX;
+                } else {
+                    mTempRect.right += offsetX;
+                }
             }
             if (mightNeedToRefill && offsetY > 0) {
                 mTempRect.top -= offsetY;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
new file mode 100644
index 0000000..ab2b90f
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LiveTileOverlay.java
@@ -0,0 +1,62 @@
+package com.android.quickstep.views;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+public class LiveTileOverlay extends Drawable {
+
+    private final Paint mPaint = new Paint();
+
+    private Rect mBoundsRect = new Rect();
+    private RectF mCurrentRect;
+    private float mCornerRadius;
+
+    private boolean mDrawEnabled = true;
+
+    public LiveTileOverlay() {
+        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
+    }
+
+    public void update(RectF currentRect, float cornerRadius) {
+        invalidateSelf();
+
+        mCurrentRect = currentRect;
+        mCornerRadius = cornerRadius;
+
+        mCurrentRect.roundOut(mBoundsRect);
+        setBounds(mBoundsRect);
+        invalidateSelf();
+    }
+
+    public void setDrawEnabled(boolean drawEnabled) {
+        if (mDrawEnabled != drawEnabled) {
+            mDrawEnabled = drawEnabled;
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        if (mCurrentRect != null && mDrawEnabled) {
+            canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
+        }
+    }
+
+    @Override
+    public void setAlpha(int i) { }
+
+    @Override
+    public void setColorFilter(ColorFilter colorFilter) { }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
similarity index 96%
rename from quickstep/src/com/android/quickstep/views/RecentsView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index 5925c4c..a7bf2c3 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -88,19 +88,19 @@
 import com.android.launcher3.util.Themes;
 import com.android.launcher3.util.ViewPool;
 import com.android.quickstep.OverviewCallbacks;
-import com.android.quickstep.QuickScrubController;
 import com.android.quickstep.RecentsAnimationWrapper;
 import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsModel.TaskThumbnailChangeListener;
 import com.android.quickstep.TaskThumbnailCache;
 import com.android.quickstep.TaskUtils;
 import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.SwipeAnimationTargetSet;
 import com.android.quickstep.util.TaskViewDrawable;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.BackgroundExecutor;
 import com.android.systemui.shared.system.PackageManagerWrapper;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.WindowCallbacksCompat;
@@ -114,7 +114,7 @@
 @TargetApi(Build.VERSION_CODES.P)
 public abstract class RecentsView<T extends BaseActivity> extends PagedView implements Insettable,
         TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
-        InvariantDeviceProfile.OnIDPChangeListener {
+        InvariantDeviceProfile.OnIDPChangeListener, TaskThumbnailChangeListener {
 
     private static final String TAG = RecentsView.class.getSimpleName();
 
@@ -152,7 +152,6 @@
     private static final float[] sTempFloatArray = new float[3];
 
     protected final T mActivity;
-    private final QuickScrubController mQuickScrubController;
     private final float mFastFlingVelocity;
     private final RecentsModel mModel;
     private final int mTaskTopMargin;
@@ -168,21 +167,11 @@
 
     private final ViewPool<TaskView> mTaskViewPool;
 
-    @Nullable Float mSimulatedVelocityX = null;
-
     /**
      * TODO: Call reloadIdNeeded in onTaskStackChanged.
      */
     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
         @Override
-        public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
-            if (!mHandleTaskStackChanges) {
-                return;
-            }
-            updateThumbnail(taskId, snapshot);
-        }
-
-        @Override
         public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
             if (!mHandleTaskStackChanges) {
                 return;
@@ -215,9 +204,6 @@
                 return;
             }
 
-            // Notify the quick scrub controller that a particular task has been removed
-            mQuickScrubController.onTaskRemoved(taskId);
-
             BackgroundExecutor.get().submit(() -> {
                 TaskView taskView = getTaskView(taskId);
                 if (taskView == null) {
@@ -269,7 +255,6 @@
 
     private boolean mOverviewStateEnabled;
     private boolean mHandleTaskStackChanges;
-    private Runnable mNextPageSwitchRunnable;
     private boolean mSwipeDownShouldLaunchApp;
     private boolean mTouchDownToStartHome;
     private final int mTouchSlop;
@@ -310,7 +295,6 @@
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
         mActivity = (T) BaseActivity.fromContext(context);
-        mQuickScrubController = new QuickScrubController(mActivity, this);
         mModel = RecentsModel.INSTANCE.get(context);
         mIdp = InvariantDeviceProfile.INSTANCE.get(context);
 
@@ -348,6 +332,19 @@
         return mIsRtl;
     }
 
+    @Override
+    public Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData) {
+        if (mHandleTaskStackChanges) {
+            TaskView taskView = getTaskView(taskId);
+            if (taskView != null) {
+                Task task = taskView.getTask();
+                taskView.getThumbnail().setThumbnail(task, thumbnailData);
+                return task;
+            }
+        }
+        return null;
+    }
+
     public TaskView updateThumbnail(int taskId, ThumbnailData thumbnailData) {
         TaskView taskView = getTaskView(taskId);
         if (taskView != null) {
@@ -379,6 +376,7 @@
         mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = new SyncRtSurfaceTransactionApplierCompat(this);
+        RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
         mIdp.addOnChangeListener(this);
     }
 
@@ -390,6 +388,7 @@
         mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
         ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
         mSyncTransactionApplier = null;
+        RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
         mIdp.removeOnChangeListener(this);
     }
 
@@ -429,17 +428,9 @@
         updateTaskStackListenerState();
     }
 
-    public void setNextPageSwitchRunnable(Runnable r) {
-        mNextPageSwitchRunnable = r;
-    }
-
     @Override
     protected void onPageEndTransition() {
         super.onPageEndTransition();
-        if (mNextPageSwitchRunnable != null) {
-            mNextPageSwitchRunnable.run();
-            mNextPageSwitchRunnable = null;
-        }
         if (getNextPage() > 0) {
             setSwipeDownShouldLaunchApp(true);
         }
@@ -550,10 +541,6 @@
             mIgnoreResetTaskId = -1;
         }
         resetTaskVisuals();
-
-        if (oldChildCount != getChildCount()) {
-            mQuickScrubController.snapToNextTaskIfAvailable();
-        }
         onTaskStackUpdated();
     }
 
@@ -605,7 +592,6 @@
         mTaskWidth = mTempRect.width();
         mTaskHeight = mTempRect.height();
 
-        // Keep this logic in sync with ActivityControlHelper.getTranslationYForQuickScrub.
         mTempRect.top -= mTaskTopMargin;
         setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top,
                 dp.widthPx - mInsets.right - mTempRect.right,
@@ -854,10 +840,6 @@
         }
     }
 
-    public QuickScrubController getQuickScrubController() {
-        return mQuickScrubController;
-    }
-
     public void setRunningTaskIconScaledDown(boolean isScaledDown) {
         if (mRunningTaskIconScaledDown != isScaledDown) {
             mRunningTaskIconScaledDown = isScaledDown;
@@ -1605,7 +1587,7 @@
             return;
         }
 
-        RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
+        SwipeAnimationTargetSet controller = mRecentsAnimationWrapper.getController();
         if (controller != null) {
             // Update the screenshot of the task
             ThumbnailData taskSnapshot = controller.screenshotTask(mRunningTaskId);
@@ -1641,18 +1623,4 @@
             }
         }
     }
-
-    public void simulateTouchEvent(MotionEvent event, @Nullable Float velocityX) {
-        mSimulatedVelocityX = velocityX;
-        dispatchTouchEvent(event);
-        mSimulatedVelocityX = null;
-    }
-
-    @Override
-    protected int computeXVelocity() {
-        if (mSimulatedVelocityX != null) {
-            return mSimulatedVelocityX.intValue();
-        }
-        return super.computeXVelocity();
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/views/TaskMenuView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskMenuView.java
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskThumbnailView.java
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
similarity index 96%
rename from quickstep/src/com/android/quickstep/views/TaskView.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 88e7ea8..fb58c24 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -29,7 +29,6 @@
 import android.animation.TimeInterpolator;
 import android.app.ActivityOptions;
 import android.content.Context;
-import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Outline;
 import android.graphics.drawable.Drawable;
@@ -215,6 +214,7 @@
      * Updates this task view to the given {@param task}.
      */
     public void bind(Task task) {
+        cancelPendingLoadTasks();
         mTask = task;
         mSnapshotView.bind(task);
     }
@@ -305,15 +305,15 @@
         if (mTask == null) {
             return;
         }
+        cancelPendingLoadTasks();
         if (visible) {
             // These calls are no-ops if the data is already loaded, try and load the high
             // resolution thumbnail if the state permits
             RecentsModel model = RecentsModel.INSTANCE.get(getContext());
             TaskThumbnailCache thumbnailCache = model.getThumbnailCache();
             TaskIconCache iconCache = model.getIconCache();
-            mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(mTask,
-                    !thumbnailCache.getHighResLoadingState().isEnabled() /* reducedResolution */,
-                    (task) -> mSnapshotView.setThumbnail(task, task.thumbnail));
+            mThumbnailLoadRequest = thumbnailCache.updateThumbnailInBackground(
+                    mTask, thumbnail -> mSnapshotView.setThumbnail(mTask, thumbnail));
             mIconLoadRequest = iconCache.updateIconInBackground(mTask,
                     (task) -> {
                         setIcon(task.icon);
@@ -325,17 +325,22 @@
                                 });
                     });
         } else {
-            if (mThumbnailLoadRequest != null) {
-                mThumbnailLoadRequest.cancel();
-            }
-            if (mIconLoadRequest != null) {
-                mIconLoadRequest.cancel();
-            }
             mSnapshotView.setThumbnail(null, null);
             setIcon(null);
         }
     }
 
+    private void cancelPendingLoadTasks() {
+        if (mThumbnailLoadRequest != null) {
+            mThumbnailLoadRequest.cancel();
+            mThumbnailLoadRequest = null;
+        }
+        if (mIconLoadRequest != null) {
+            mIconLoadRequest.cancel();
+            mIconLoadRequest = null;
+        }
+    }
+
     private boolean showTaskMenu() {
         getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
         mMenuView = TaskMenuView.showForTask(this);
@@ -412,10 +417,7 @@
 
     public void resetVisualProperties() {
         resetViewTransforms();
-        if (!getRecentsView().getQuickScrubController().isQuickSwitch()) {
-            // Reset full screen progress unless we are doing back to back quick switch.
-            setFullscreenProgress(0);
-        }
+        setFullscreenProgress(0);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 0007b37..ab5f479 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -59,10 +59,10 @@
 import com.android.launcher3.allapps.AllAppsTransitionController;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
-import com.android.launcher3.graphics.DrawableFactory;
 import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.RecentsModel;
 import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
@@ -112,7 +112,6 @@
     private static final int APP_LAUNCH_ALPHA_DURATION = 50;
 
     public static final int RECENTS_LAUNCH_DURATION = 336;
-    public static final int RECENTS_QUICKSCRUB_LAUNCH_DURATION = 300;
     private static final int LAUNCHER_RESUME_START_DELAY = 100;
     private static final int CLOSING_TRANSITION_DURATION_MS = 250;
 
@@ -135,7 +134,7 @@
     private final float mClosingWindowTransY;
 
     private DeviceProfile mDeviceProfile;
-    private View mFloatingView;
+    private FloatingIconView mFloatingView;
 
     private RemoteAnimationProvider mRemoteAnimationProvider;
 
@@ -181,10 +180,6 @@
     public ActivityOptions getActivityLaunchOptions(Launcher launcher, View v) {
         if (hasControlRemoteAppTransitionPermission()) {
             boolean fromRecents = isLaunchingFromRecents(v, null /* targets */);
-            if (fromRecents && isQuickSwitchInProgress()) {
-                return getQuickSwitchActivityOptions();
-            }
-
             RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler,
                     true /* startAtFrontOfQueue */) {
 
@@ -238,20 +233,6 @@
             @Nullable RemoteAnimationTargetCompat[] targets);
 
     /**
-     * Whether a quick scrub is in progress.
-     *
-     * @return true if in progress
-     */
-    protected abstract boolean isQuickSwitchInProgress();
-
-    /**
-     * Get activity options for a quick switch launch that include the launch animation.
-     *
-     * @return the activity options for a quick switch recents launch
-     */
-    protected abstract ActivityOptions getQuickSwitchActivityOptions();
-
-    /**
      * Composes the animations for a launch from the recents list.
      *
      * @param anim the animator set to add to
@@ -423,7 +404,7 @@
             boolean toggleVisibility) {
         final boolean isBubbleTextView = v instanceof BubbleTextView;
         if (mFloatingView == null) {
-            mFloatingView = new View(mLauncher);
+            mFloatingView = new FloatingIconView(mLauncher);
         } else {
             mFloatingView.setTranslationX(0);
             mFloatingView.setTranslationY(0);
@@ -432,58 +413,15 @@
             mFloatingView.setAlpha(1);
             mFloatingView.setBackground(null);
         }
-        if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
-            // Create a copy of the app icon
-            mFloatingView.setBackground(DrawableFactory.INSTANCE.get(mLauncher)
-                    .newIcon(v.getContext(), (ItemInfoWithIcon) v.getTag()));
-        }
-
-        // Position the floating view exactly on top of the original
         Rect rect = new Rect();
-        final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
-        if (fromDeepShortcutView) {
-            // Deep shortcut views have their icon drawn in a separate view.
-            DeepShortcutView view = (DeepShortcutView) v.getParent();
-            mDragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
-        } else {
-            mDragLayer.getDescendantRectRelativeToSelf(v, rect);
-        }
-        int viewLocationLeft = rect.left;
-        int viewLocationTop = rect.top;
+        mFloatingView.matchPositionOf(mLauncher, v, toggleVisibility, rect);
 
-        float startScale = 1f;
-        if (isBubbleTextView && !fromDeepShortcutView) {
-            BubbleTextView btv = (BubbleTextView) v;
-            btv.getIconBounds(rect);
-            Drawable dr = btv.getIcon();
-            if (dr instanceof FastBitmapDrawable) {
-                startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
-            }
-        } else {
-            rect.set(0, 0, rect.width(), rect.height());
-        }
-        viewLocationLeft += rect.left;
-        viewLocationTop += rect.top;
-        int viewLocationStart = mIsRtl
-                ? windowTargetBounds.width() - rect.right
-                : viewLocationLeft;
-        LayoutParams lp = new LayoutParams(rect.width(), rect.height());
-        lp.ignoreInsets = true;
-        lp.leftMargin = viewLocationStart;
-        lp.topMargin = viewLocationTop;
+        int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
+        LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
+        // Special RTL logic is needed to handle the window target bounds.
+        lp.leftMargin = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
         mFloatingView.setLayoutParams(lp);
 
-        // Set the properties here already to make sure they'are available when running the first
-        // animation frame.
-        mFloatingView.layout(viewLocationLeft, viewLocationTop,
-                viewLocationLeft + rect.width(), viewLocationTop + rect.height());
-
-        // Swap the two views in place.
-        ((ViewGroup) mDragLayer.getParent()).getOverlay().add(mFloatingView);
-        if (toggleVisibility) {
-            v.setVisibility(View.INVISIBLE);
-        }
-
         int[] dragLayerBounds = new int[2];
         mDragLayer.getLocationOnScreen(dragLayerBounds);
 
@@ -494,8 +432,8 @@
         float xPosition = mIsRtl
                 ? windowTargetBounds.width() - lp.getMarginStart() - rect.width()
                 : lp.getMarginStart();
-        float dX = centerX - xPosition - (lp.width / 2);
-        float dY = centerY - lp.topMargin - (lp.height / 2);
+        float dX = centerX - xPosition - (lp.width / 2f);
+        float dY = centerY - lp.topMargin - (lp.height / 2f);
 
         ObjectAnimator x = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_X, 0f, dX);
         ObjectAnimator y = ObjectAnimator.ofFloat(mFloatingView, View.TRANSLATION_Y, 0f, dY);
@@ -521,6 +459,14 @@
         float maxScaleX = windowTargetBounds.width() / (float) rect.width();
         float maxScaleY = windowTargetBounds.height() / (float) rect.height();
         float scale = Math.max(maxScaleX, maxScaleY);
+        float startScale = 1f;
+        if (isBubbleTextView && !(v.getParent() instanceof DeepShortcutView)) {
+            Drawable dr = ((BubbleTextView) v).getIcon();
+            if (dr instanceof FastBitmapDrawable) {
+                startScale = ((FastBitmapDrawable) dr).getAnimatedScale();
+            }
+        }
+
         ObjectAnimator scaleAnim = ObjectAnimator
                 .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale);
         scaleAnim.setDuration(APP_LAUNCH_DURATION)
@@ -540,6 +486,7 @@
         alpha.setInterpolator(LINEAR);
         appOpenAnimator.play(alpha);
 
+        appOpenAnimator.addListener(mFloatingView);
         appOpenAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 25e0af2..1eaa8bc 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -15,10 +15,8 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.launcher3.AbstractFloatingView.TYPE_QUICKSTEP_PREVIEW;
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
 
 import com.android.launcher3.AbstractFloatingView;
 import com.android.launcher3.Launcher;
@@ -46,11 +44,7 @@
 
     @Override
     public void onStateEnabled(Launcher launcher) {
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            AbstractFloatingView.closeAllOpenViews(launcher);
-        } else {
-            AbstractFloatingView.closeAllOpenViewsExcept(launcher, TYPE_QUICKSTEP_PREVIEW);
-        }
+        AbstractFloatingView.closeAllOpenViews(launcher);
         dispatchWindowStateChanged(launcher);
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 70aae13..25e1c89 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -125,8 +125,8 @@
 
     public static void onEnterAnimationComplete(Context context) {
         // After the transition to home, enable the high-res thumbnail loader if it wasn't enabled
-        // as a part of quickstep/scrub, so that high-res thumbnails can load the next time we
-        // enter overview
+        // as a part of quickstep, so that high-res thumbnails can load the next time we enter
+        // overview
         RecentsModel.INSTANCE.get(context).getThumbnailCache()
                 .getHighResLoadingState().setVisible(true);
     }
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 0bdb578..eccef04 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -17,57 +17,41 @@
 
 import android.animation.Animator;
 import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.graphics.Region;
 import android.os.Build;
 import android.os.Handler;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.UiThread;
-
 import com.android.launcher3.BaseDraggingActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
-import com.android.quickstep.TouchConsumer.InteractionType;
 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 java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.UiThread;
+
 /**
  * Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
  */
 @TargetApi(Build.VERSION_CODES.P)
 public interface ActivityControlHelper<T extends BaseDraggingActivity> {
 
-    LayoutListener createLayoutListener(T activity);
-
-    /**
-     * Updates the UI to indicate quick interaction.
-     */
-    void onQuickInteractionStart(T activity, @Nullable RunningTaskInfo taskInfo,
-            boolean activityVisible, TouchInteractionLog touchInteractionLog);
-
-    float getTranslationYForQuickScrub(TransformedRect targetRect, DeviceProfile dp,
-            Context context);
-
-    void executeOnWindowAvailable(T activity, Runnable action);
-
     void onTransitionCancelled(T activity, boolean activityVisible);
 
-    int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            @InteractionType int interactionType, TransformedRect outRect);
+    int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
 
     void onSwipeUpComplete(T activity);
 
@@ -91,46 +75,25 @@
     <T extends View> T getVisibleRecentsView();
 
     @UiThread
-    boolean switchToRecentsIfVisible(boolean fromRecentsButton);
+    boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
 
     Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
 
     boolean shouldMinimizeSplitScreen();
 
-    /**
-     * @return {@code true} if recents activity should be started immediately on touchDown,
-     *         {@code false} if it should deferred until some threshold is crossed.
-     */
-    boolean deferStartingActivity(int downHitTarget);
-
-    boolean supportsLongSwipe(T activity);
+    default boolean deferStartingActivity(Region activeNavBarRegion, MotionEvent ev) {
+        return true;
+    }
 
     AlphaProperty getAlphaProperty(T activity);
 
     /**
-     * Must return a non-null controller is supportsLongSwipe was true.
-     */
-    LongSwipeHelper getLongSwipeController(T activity, int runningTaskId);
-
-    /**
      * Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
      */
     int getContainerType();
 
     boolean isInLiveTileMode();
 
-    interface LayoutListener {
-
-        void open();
-
-        void setHandler(WindowTransformSwipeHandler handler);
-
-        void finish();
-
-        void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect,
-                float cornerRadius);
-    }
-
     interface ActivityInitListener {
 
         void register();
@@ -155,7 +118,7 @@
 
         default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { }
 
-        void createActivityController(long transitionLength, @InteractionType int interactionType);
+        void createActivityController(long transitionLength);
 
         default void onTransitionCancelled() { }
 
@@ -165,6 +128,11 @@
 
     interface HomeAnimationFactory {
 
+        /** Return the floating view that will animate in sync with the closing window. */
+        default @Nullable View getFloatingView() {
+            return null;
+        }
+
         @NonNull RectF getWindowTargetRect();
 
         @NonNull Animator createActivityAnimationToHome();
diff --git a/quickstep/src/com/android/quickstep/BaseRecentsActivity.java b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
new file mode 100644
index 0000000..c840132
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/BaseRecentsActivity.java
@@ -0,0 +1,162 @@
+/*
+ * 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 android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.os.Bundle;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.R;
+import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.util.SystemUiController;
+import com.android.launcher3.util.Themes;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * A base fallback recents activity that provides support for device profile changes, activity
+ * lifecycle tracking, and basic input handling from recents.
+ *
+ * This class is only used as a fallback in case the default launcher does not have a recents
+ * implementation.
+ */
+public abstract class BaseRecentsActivity extends BaseDraggingActivity {
+
+    private Configuration mOldConfig;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mOldConfig = new Configuration(getResources().getConfiguration());
+        initDeviceProfile();
+        initViews();
+
+        getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
+                Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
+        RecentsActivityTracker.onRecentsActivityCreate(this);
+    }
+
+    /**
+     * Init drag layer and overview panel views.
+     */
+    abstract protected void initViews();
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        int diff = newConfig.diff(mOldConfig);
+        if ((diff & (CONFIG_ORIENTATION | CONFIG_SCREEN_SIZE)) != 0) {
+            onHandleConfigChanged();
+        }
+        mOldConfig.setTo(newConfig);
+        super.onConfigurationChanged(newConfig);
+    }
+
+    /**
+     * Logic for when device configuration changes (rotation, screen size change, multi-window,
+     * etc.)
+     */
+    protected void onHandleConfigChanged() {
+        mUserEventDispatcher = null;
+        initDeviceProfile();
+
+        AbstractFloatingView.closeOpenViews(this, true,
+                AbstractFloatingView.TYPE_ALL & ~AbstractFloatingView.TYPE_REBIND_SAFE);
+        dispatchDeviceProfileChanged();
+
+        reapplyUi();
+    }
+
+    /**
+     * Initialize/update the device profile.
+     */
+    private void initDeviceProfile() {
+        mDeviceProfile = createDeviceProfile();
+        onDeviceProfileInitiated();
+    }
+
+    /**
+     * Generate the device profile to use in this activity.
+     * @return device profile
+     */
+    protected DeviceProfile createDeviceProfile() {
+        DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
+
+        // In case we are reusing IDP, create a copy so that we don't conflict with Launcher
+        // activity.
+        return dp.copy(this);
+    }
+
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        // Workaround for b/78520668, explicitly trim memory once UI is hidden
+        onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
+    }
+
+    @Override
+    public void onEnterAnimationComplete() {
+        super.onEnterAnimationComplete();
+        UiFactory.onEnterAnimationComplete(this);
+    }
+
+    @Override
+    public void onTrimMemory(int level) {
+        super.onTrimMemory(level);
+        UiFactory.onTrimMemory(this, level);
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        RecentsActivityTracker.onRecentsActivityNewIntent(this);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        RecentsActivityTracker.onRecentsActivityDestroy(this);
+    }
+
+    @Override
+    public void onBackPressed() {
+        // TODO: Launch the task we came from
+        startHome();
+    }
+
+    public void startHome() {
+        startActivity(new Intent(Intent.ACTION_MAIN)
+                .addCategory(Intent.CATEGORY_HOME)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    @Override
+    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+        super.dump(prefix, fd, writer, args);
+        writer.println(prefix + "Misc:");
+        dumpMisc(writer);
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/LongSwipeHelper.java b/quickstep/src/com/android/quickstep/LongSwipeHelper.java
deleted file mode 100644
index ef56cb0..0000000
--- a/quickstep/src/com/android/quickstep/LongSwipeHelper.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * 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;
-
-import static com.android.launcher3.LauncherAnimUtils.MIN_PROGRESS_TO_ALL_APPS;
-import static com.android.launcher3.LauncherState.ALL_APPS;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.anim.Interpolators.DEACCEL;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
-import static com.android.quickstep.WindowTransformSwipeHandler.MAX_SWIPE_DURATION;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_OVERSHOOT_DURATION;
-
-import android.animation.ValueAnimator;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherStateManager;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.allapps.AllAppsTransitionController;
-import com.android.launcher3.allapps.DiscoveryBounce;
-import com.android.launcher3.anim.AnimatorPlaybackController;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
-import com.android.launcher3.anim.Interpolators.OvershootParams;
-import com.android.launcher3.uioverrides.PortraitStatesTouchController;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.util.FlingBlockCheck;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Utility class to handle long swipe from an app.
- * This assumes the presence of Launcher activity as long swipe is not supported on the
- * fallback activity.
- */
-public class LongSwipeHelper {
-
-    private static final float SWIPE_DURATION_MULTIPLIER =
-            Math.min(1 / MIN_PROGRESS_TO_ALL_APPS, 1 / (1 - MIN_PROGRESS_TO_ALL_APPS));
-
-    private final Launcher mLauncher;
-    private final int mRunningTaskId;
-
-    private float mMaxSwipeDistance = 1;
-    private AnimatorPlaybackController mAnimator;
-    private FlingBlockCheck mFlingBlockCheck = new FlingBlockCheck();
-
-    LongSwipeHelper(Launcher launcher, int runningTaskId) {
-        mLauncher = launcher;
-        mRunningTaskId = runningTaskId;
-        init();
-    }
-
-    private void init() {
-        mFlingBlockCheck.blockFling();
-
-        // Init animations
-        AllAppsTransitionController controller = mLauncher.getAllAppsController();
-        // TODO: Scale it down so that we can reach all-apps in screen space
-        mMaxSwipeDistance = Math.max(1, controller.getProgress() * controller.getShiftRange());
-
-        AnimatorSetBuilder builder = PortraitStatesTouchController.getOverviewToAllAppsAnimation();
-        mAnimator = mLauncher.getStateManager().createAnimationToNewWorkspace(ALL_APPS, builder,
-                Math.round(2 * mMaxSwipeDistance), null, LauncherStateManager.ANIM_ALL);
-        mAnimator.dispatchOnStart();
-    }
-
-    public void onMove(float displacement) {
-        mAnimator.setPlayFraction(displacement / mMaxSwipeDistance);
-        mFlingBlockCheck.onEvent();
-    }
-
-    public void destroy() {
-        // TODO: We can probably also show the task view
-
-        mLauncher.getStateManager().goToState(OVERVIEW, false);
-    }
-
-    public void end(float velocity, boolean isFling, Runnable callback) {
-        float velocityPxPerMs = velocity / 1000;
-        long duration = MAX_SWIPE_DURATION;
-        Interpolator interpolator = DEACCEL;
-
-        final float currentFraction = mAnimator.getProgressFraction();
-        final boolean toAllApps;
-        float endProgress;
-
-        boolean blockedFling = isFling && mFlingBlockCheck.isBlocked();
-        if (blockedFling) {
-            isFling = false;
-        }
-
-        if (!isFling) {
-            toAllApps = currentFraction > MIN_PROGRESS_TO_ALL_APPS;
-            endProgress = toAllApps ? 1 : 0;
-
-            long expectedDuration = Math.abs(Math.round((endProgress - currentFraction)
-                    * MAX_SWIPE_DURATION * SWIPE_DURATION_MULTIPLIER));
-            duration = Math.min(MAX_SWIPE_DURATION, expectedDuration);
-
-            if (blockedFling && !toAllApps && !QUICKSTEP_SPRINGS.get()) {
-                Interpolators.OvershootParams overshoot = new OvershootParams(currentFraction,
-                        currentFraction, endProgress, velocityPxPerMs, (int) mMaxSwipeDistance);
-                duration = (overshoot.duration + duration);
-                duration = Utilities.boundToRange(duration, MIN_OVERSHOOT_DURATION,
-                        MAX_SWIPE_DURATION);
-                interpolator = overshoot.interpolator;
-                endProgress = overshoot.end;
-            }
-        } else {
-            toAllApps = velocity < 0;
-            endProgress = toAllApps ? 1 : 0;
-
-            float minFlingVelocity = mLauncher.getResources()
-                    .getDimension(R.dimen.quickstep_fling_min_velocity);
-            if (Math.abs(velocity) > minFlingVelocity && mMaxSwipeDistance > 0) {
-                float distanceToTravel = (endProgress - currentFraction) * mMaxSwipeDistance;
-
-                // we want the page's snap velocity to approximately match the velocity at
-                // which the user flings, so we scale the duration by a value near to the
-                // derivative of the scroll interpolator at zero, ie. 2.
-                long baseDuration = Math.round(Math.abs(distanceToTravel / velocityPxPerMs));
-                duration = Math.min(MAX_SWIPE_DURATION, 2 * baseDuration);
-            }
-        }
-
-        final boolean finalIsFling = isFling;
-        mAnimator.setEndAction(() -> onSwipeAnimationComplete(toAllApps, finalIsFling, callback));
-
-        ValueAnimator animator = mAnimator.getAnimationPlayer();
-        animator.setDuration(duration).setInterpolator(interpolator);
-        animator.setFloatValues(currentFraction, endProgress);
-
-        if (QUICKSTEP_SPRINGS.get()) {
-            mAnimator.dispatchOnStartWithVelocity(endProgress, velocityPxPerMs);
-        }
-        animator.start();
-    }
-
-    private void onSwipeAnimationComplete(boolean toAllApps, boolean isFling, Runnable callback) {
-        RecentsView rv = mLauncher.getOverviewPanel();
-        if (!toAllApps) {
-            rv.setIgnoreResetTask(mRunningTaskId);
-        }
-
-        mLauncher.getStateManager().goToState(toAllApps ? ALL_APPS : OVERVIEW, false);
-        if (!toAllApps) {
-            DiscoveryBounce.showForOverviewIfNeeded(mLauncher);
-            rv.animateUpRunningTaskIconScale();
-            rv.setSwipeDownShouldLaunchApp(true);
-        }
-
-        mLauncher.getUserEventDispatcher().logStateChangeAction(
-                isFling ? Touch.FLING : Touch.SWIPE, Direction.UP,
-                ContainerType.NAVBAR, ContainerType.APP,
-                toAllApps ? ContainerType.ALLAPPS : ContainerType.TASKSWITCHER,
-                0);
-
-        callback.run();
-
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get() && toAllApps) {
-            rv.finishRecentsAnimation(true, null);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
deleted file mode 100644
index 3664c97..0000000
--- a/quickstep/src/com/android/quickstep/MotionEventQueue.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.view.MotionEvent.ACTION_MASK;
-import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.os.Looper;
-import android.util.Log;
-import android.util.Pair;
-import android.view.Choreographer;
-import android.view.InputEvent;
-import android.view.MotionEvent;
-
-import com.android.systemui.shared.system.InputChannelCompat;
-import com.android.systemui.shared.system.InputChannelCompat.InputEventDispatcher;
-import com.android.systemui.shared.system.InputChannelCompat.InputEventReceiver;
-import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
-
-/**
- * Helper class for batching input events
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class MotionEventQueue {
-
-    private static final String TAG = "MotionEventQueue";
-
-    private static final int ACTION_VIRTUAL = ACTION_MASK - 1;
-
-    private static final int ACTION_QUICK_SCRUB_START =
-            ACTION_VIRTUAL | (1 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_QUICK_SCRUB_PROGRESS =
-            ACTION_VIRTUAL | (2 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_QUICK_SCRUB_END =
-            ACTION_VIRTUAL | (3 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_NEW_GESTURE =
-            ACTION_VIRTUAL | (4 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_SHOW_OVERVIEW_FROM_ALT_TAB =
-            ACTION_VIRTUAL | (5 << ACTION_POINTER_INDEX_SHIFT);
-    private static final int ACTION_QUICK_STEP =
-            ACTION_VIRTUAL | (6 << ACTION_POINTER_INDEX_SHIFT);
-
-    private final InputEventDispatcher mDispatcher;
-    private final InputEventReceiver mReceiver;
-    private final ConsumerFactory mConsumerFactory;
-
-    private TouchConsumer mConsumer;
-
-    public MotionEventQueue(Looper looper, Choreographer choreographer,
-            ConsumerFactory consumerFactory) {
-        Pair<InputEventDispatcher, InputEventReceiver> pair = InputChannelCompat.createPair(
-                "sysui-callbacks", looper, choreographer, this::onInputEvent);
-
-        mConsumerFactory = consumerFactory;
-        mConsumer = TouchConsumer.NO_OP;
-        mDispatcher = pair.first;
-        mReceiver = pair.second;
-    }
-
-    private void onInputEvent(InputEvent ev) {
-        if (!(ev instanceof MotionEvent)) {
-            throw new IllegalStateException("Unknown event " + ev);
-        }
-        MotionEvent event = (MotionEvent) ev;
-        if (event.getActionMasked() == ACTION_VIRTUAL) {
-            switch (event.getAction()) {
-                case ACTION_QUICK_SCRUB_START:
-                    mConsumer.onQuickScrubStart();
-                    break;
-                case ACTION_QUICK_SCRUB_PROGRESS:
-                    mConsumer.onQuickScrubProgress(event.getX());
-                    break;
-                case ACTION_QUICK_SCRUB_END:
-                    mConsumer.onQuickScrubEnd();
-                    break;
-                case ACTION_NEW_GESTURE: {
-                    boolean useSharedState = mConsumer.isActive();
-                    mConsumer.onConsumerAboutToBeSwitched();
-                    mConsumer = mConsumerFactory.newConsumer(event.getSource(), useSharedState);
-                    break;
-                }
-                case ACTION_SHOW_OVERVIEW_FROM_ALT_TAB:
-                    mConsumer.onShowOverviewFromAltTab();
-                    mConsumer.onQuickScrubStart();
-                    break;
-                case ACTION_QUICK_STEP:
-                    mConsumer.onQuickStep(event);
-                    break;
-                default:
-                    Log.e(TAG, "Invalid virtual event: " + event.getAction());
-            }
-        } else {
-            mConsumer.accept(event);
-        }
-    }
-
-    public void queue(MotionEvent event) {
-        mDispatcher.dispatch(event);
-    }
-
-    private void queueVirtualAction(int action, float param) {
-        queue(MotionEvent.obtain(0, 0, action, param, 0, 0));
-    }
-
-    private void queueVirtualAction(int action, int param) {
-        MotionEvent ev = MotionEvent.obtain(0, 0, action, 0, 0, 0);
-        ev.setSource(param);
-        queue(ev);
-    }
-
-    public void onQuickScrubStart() {
-        queueVirtualAction(ACTION_QUICK_SCRUB_START, 0);
-    }
-
-    public void onOverviewShownFromAltTab() {
-        queueVirtualAction(ACTION_SHOW_OVERVIEW_FROM_ALT_TAB, 0);
-    }
-
-    public void onQuickScrubProgress(float progress) {
-        queueVirtualAction(ACTION_QUICK_SCRUB_PROGRESS, progress);
-    }
-
-    public void onQuickScrubEnd() {
-        queueVirtualAction(ACTION_QUICK_SCRUB_END, 0);
-    }
-
-    public void onQuickStep(MotionEvent event) {
-        event.setAction(ACTION_QUICK_STEP);
-        queue(event);
-    }
-
-    public void onNewGesture(@HitTarget int downHitTarget) {
-        queueVirtualAction(ACTION_NEW_GESTURE, downHitTarget);
-    }
-
-    /**
-     * To be called by the consumer when it's no longer active.
-     */
-    public void onConsumerInactive(TouchConsumer caller) {
-        if (mConsumer == caller) {
-            mConsumer = TouchConsumer.NO_OP;
-        }
-    }
-
-    public void dispose() {
-        mDispatcher.dispose();
-        mReceiver.dispose();
-    }
-
-    public interface ConsumerFactory {
-
-        TouchConsumer newConsumer(@HitTarget int downHitTarget, boolean useSharedState);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index b6741f4..724a492 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -96,7 +96,7 @@
         } else {
             // The default home app is a different launcher. Use the fallback Overview instead.
             overviewComponent = new ComponentName(mContext, RecentsActivity.class);
-            mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
+            mActivityControlHelper = new FallbackActivityControllerHelper();
             overviewIntentCategory = Intent.CATEGORY_DEFAULT;
 
             // User's default home app can change as a result of package updates of this app (such
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index 27f1399..411e593 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -64,8 +64,6 @@
     private final Handler mUiHandler;
     private final Handler mBgHandler;
 
-    private boolean mSwipeGestureInitializing = false;
-
     // These are updated on the background thread
     private ISystemUiProxy mISystemUiProxy;
     private boolean mSwipeUpEnabled = true;
@@ -154,7 +152,7 @@
             return;
         }
 
-        int flags = 0;
+        int flags = FLAG_DISABLE_QUICK_SCRUB;
         if (!mSwipeUpEnabled) {
             flags = FLAG_DISABLE_SWIPE_UP | FLAG_DISABLE_QUICK_SCRUB | FLAG_SHOW_OVERVIEW_BUTTON;
         }
@@ -177,15 +175,6 @@
         }
     }
 
-    @WorkerThread
-    public void setSwipeGestureInitializing(boolean swipeGestureInitializing) {
-        mSwipeGestureInitializing = swipeGestureInitializing;
-    }
-
-    public boolean swipeGestureInitializing() {
-        return mSwipeGestureInitializing;
-    }
-
     public void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
         mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
         mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, swipeUpEnabled ? 1 : 0, 0).
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
deleted file mode 100644
index db0150e..0000000
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * 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;
-
-import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
-import static com.android.launcher3.anim.Interpolators.ACCEL;
-import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
-import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.util.FloatProperty;
-import android.util.Log;
-import android.view.HapticFeedbackConstants;
-import android.view.animation.Interpolator;
-
-import com.android.launcher3.Alarm;
-import com.android.launcher3.BaseActivity;
-import com.android.launcher3.OnAlarmListener;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-
-/**
- * Responds to quick scrub callbacks to page through and launch recent tasks.
- *
- * The behavior is to evenly divide the progress into sections, each of which scrolls one page.
- * The first and last section set an alarm to auto-advance backwards or forwards, respectively.
- */
-@TargetApi(Build.VERSION_CODES.P)
-public class QuickScrubController implements OnAlarmListener {
-
-    public static final int QUICK_SWITCH_FROM_APP_START_DURATION = 0;
-    public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
-    public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 200;
-    // We want the translation y to finish faster than the rest of the animation.
-    public static final float QUICK_SCRUB_TRANSLATION_Y_FACTOR = 5f / 6;
-    public static final Interpolator QUICK_SCRUB_START_INTERPOLATOR = FAST_OUT_SLOW_IN;
-
-    /**
-     * Snap to a new page when crossing these thresholds. The first and last auto-advance.
-     */
-    private static final float[] QUICK_SCRUB_THRESHOLDS = new float[] {
-            0.05f, 0.20f, 0.35f, 0.50f, 0.65f, 0.80f, 0.95f
-    };
-
-    private static final FloatProperty<QuickScrubController> PROGRESS
-            = new FloatProperty<QuickScrubController>("progress") {
-        @Override
-        public void setValue(QuickScrubController quickScrubController, float progress) {
-            quickScrubController.onQuickScrubProgress(progress);
-        }
-
-        @Override
-        public Float get(QuickScrubController quickScrubController) {
-            return quickScrubController.mEndProgress;
-        }
-    };
-
-    private static final String TAG = "QuickScrubController";
-    private static final boolean ENABLE_AUTO_ADVANCE = true;
-    private static final long AUTO_ADVANCE_DELAY = 500;
-    private static final int QUICKSCRUB_SNAP_DURATION_PER_PAGE = 325;
-    private static final int QUICKSCRUB_END_SNAP_DURATION_PER_PAGE = 60;
-
-    private final Alarm mAutoAdvanceAlarm;
-    private final RecentsView mRecentsView;
-    private final BaseActivity mActivity;
-
-    private boolean mInQuickScrub;
-    private boolean mWaitingForTaskLaunch;
-    private int mQuickScrubSection;
-    private boolean mStartedFromHome;
-    private boolean mFinishedTransitionToQuickScrub;
-    private int mLaunchingTaskId;
-    private Runnable mOnFinishedTransitionToQuickScrubRunnable;
-    private ActivityControlHelper mActivityControlHelper;
-    private TouchInteractionLog mTouchInteractionLog;
-
-    private boolean mIsQuickSwitch;
-    private float mStartProgress;
-    private float mEndProgress;
-    private float mPrevProgressDelta;
-    private float mPrevPrevProgressDelta;
-    private boolean mShouldSwitchToNext;
-
-    public QuickScrubController(BaseActivity activity, RecentsView recentsView) {
-        mActivity = activity;
-        mRecentsView = recentsView;
-        if (ENABLE_AUTO_ADVANCE) {
-            mAutoAdvanceAlarm = new Alarm();
-            mAutoAdvanceAlarm.setOnAlarmListener(this);
-        }
-    }
-
-    public void onQuickScrubStart(boolean startingFromHome, ActivityControlHelper controlHelper,
-            TouchInteractionLog touchInteractionLog) {
-        prepareQuickScrub(TAG);
-        mInQuickScrub = true;
-        mStartedFromHome = startingFromHome;
-        mQuickScrubSection = 0;
-        mFinishedTransitionToQuickScrub = false;
-        mActivityControlHelper = controlHelper;
-        mTouchInteractionLog = touchInteractionLog;
-
-        if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (mRecentsView.getRunningTaskView() != null) {
-                mRecentsView.getRunningTaskView().setShowScreenshot(false);
-            }
-        }
-
-        if (mIsQuickSwitch) {
-            mShouldSwitchToNext = true;
-            mPrevProgressDelta = 0;
-            TaskView runningTaskView = mRecentsView.getRunningTaskView();
-            TaskView nextTaskView = mRecentsView.getNextTaskView();
-            if (runningTaskView != null) {
-                runningTaskView.setFullscreenProgress(1);
-            }
-            if (nextTaskView != null) {
-                nextTaskView.setFullscreenProgress(1);
-            }
-        }
-
-        snapToNextTaskIfAvailable();
-        mActivity.getUserEventDispatcher().resetActionDurationMillis();
-    }
-
-    public void onQuickScrubEnd() {
-        mInQuickScrub = false;
-
-        Runnable launchTaskRunnable = () -> {
-            int page = mRecentsView.getPageNearestToCenterOfScreen();
-            TaskView taskView = mRecentsView.getTaskViewAt(page);
-            if (taskView != null) {
-                mWaitingForTaskLaunch = true;
-                mTouchInteractionLog.launchTaskStart();
-                mLaunchingTaskId = taskView.getTask().key.id;
-                taskView.launchTask(true, (result) -> {
-                    mTouchInteractionLog.launchTaskEnd(result);
-                    if (!result) {
-                        taskView.notifyTaskLaunchFailed(TAG);
-                        breakOutOfQuickScrub();
-                    } else {
-                        mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
-                                LauncherLogProto.Action.Direction.NONE, page,
-                                TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key));
-                    }
-                    mWaitingForTaskLaunch = false;
-                    if (mIsQuickSwitch) {
-                        mIsQuickSwitch = false;
-                        TaskView runningTaskView = mRecentsView.getRunningTaskView();
-                        TaskView nextTaskView = mRecentsView.getNextTaskView();
-                        if (runningTaskView != null) {
-                            runningTaskView.setFullscreenProgress(0);
-                        }
-                        if (nextTaskView != null) {
-                            nextTaskView.setFullscreenProgress(0);
-                        }
-                    }
-
-                }, taskView.getHandler());
-            } else {
-                breakOutOfQuickScrub();
-            }
-            mActivityControlHelper = null;
-        };
-
-        if (mIsQuickSwitch) {
-            float progressVelocity = mPrevPrevProgressDelta / SINGLE_FRAME_MS;
-            // Move to the next frame immediately, then start the animation from the
-            // following frame since it starts a frame later.
-            float singleFrameProgress = progressVelocity * SINGLE_FRAME_MS;
-            float fromProgress = mEndProgress + singleFrameProgress;
-            onQuickScrubProgress(fromProgress);
-            fromProgress += singleFrameProgress;
-            float toProgress = mShouldSwitchToNext ? 1 : 0;
-            int duration = (int) Math.abs((toProgress - fromProgress) / progressVelocity);
-            duration = Utilities.boundToRange(duration, 80, 300);
-            Animator anim = ObjectAnimator.ofFloat(this, PROGRESS, fromProgress, toProgress);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    launchTaskRunnable.run();
-                }
-            });
-            anim.setDuration(duration).start();
-            return;
-        }
-
-        if (ENABLE_AUTO_ADVANCE) {
-            mAutoAdvanceAlarm.cancelAlarm();
-        }
-        int page = mRecentsView.getNextPage();
-        int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
-                * QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
-        if (mRecentsView.getChildCount() > 0 && mRecentsView.snapToPage(page, snapDuration)) {
-            // Settle on the page then launch it
-            mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
-        } else {
-            // No page move needed, just launch it
-            if (mFinishedTransitionToQuickScrub) {
-                launchTaskRunnable.run();
-            } else {
-                mOnFinishedTransitionToQuickScrubRunnable = launchTaskRunnable;
-            }
-        }
-    }
-
-    public void cancelActiveQuickscrub() {
-        if (!mInQuickScrub) {
-            return;
-        }
-        Log.d(TAG, "Quickscrub was active, cancelling");
-        mInQuickScrub = false;
-        mActivityControlHelper = null;
-        mOnFinishedTransitionToQuickScrubRunnable = null;
-        mRecentsView.setNextPageSwitchRunnable(null);
-        mLaunchingTaskId = 0;
-    }
-
-    public boolean prepareQuickScrub(String tag) {
-        return prepareQuickScrub(tag, mIsQuickSwitch);
-    }
-
-    /**
-     * Initializes the UI for quick scrub, returns true if success.
-     */
-    public boolean prepareQuickScrub(String tag, boolean isQuickSwitch) {
-        if (mWaitingForTaskLaunch || mInQuickScrub) {
-            Log.d(tag, "Waiting for last scrub to finish, will skip this interaction");
-            return false;
-        }
-        mOnFinishedTransitionToQuickScrubRunnable = null;
-        mRecentsView.setNextPageSwitchRunnable(null);
-        mIsQuickSwitch = isQuickSwitch;
-        return true;
-    }
-
-    public boolean isQuickSwitch() {
-        return mIsQuickSwitch;
-    }
-
-    public boolean isWaitingForTaskLaunch() {
-        return mWaitingForTaskLaunch;
-    }
-
-    /**
-     * Attempts to go to normal overview or back to home, so UI doesn't prevent user interaction.
-     */
-    private void breakOutOfQuickScrub() {
-        if (mRecentsView.getChildCount() == 0 || mActivityControlHelper == null
-                || !mActivityControlHelper.switchToRecentsIfVisible(false)) {
-            mActivity.onBackPressed();
-        }
-    }
-
-    public void onQuickScrubProgress(float progress) {
-        if (mIsQuickSwitch) {
-            TaskView currentPage = mRecentsView.getRunningTaskView();
-            TaskView nextPage = mRecentsView.getNextTaskView();
-            if (currentPage == null || nextPage == null) {
-                return;
-            }
-            if (!mFinishedTransitionToQuickScrub || mStartProgress <= 0) {
-                mStartProgress = mEndProgress = progress;
-            } else {
-                float progressDelta = progress - mEndProgress;
-                mEndProgress = progress;
-                progress = Utilities.boundToRange(progress, mStartProgress, 1);
-                progress = Utilities.mapToRange(progress, mStartProgress, 1, 0, 1, LINEAR);
-                if (mInQuickScrub) {
-                    mShouldSwitchToNext = mPrevProgressDelta > 0.007f || progressDelta > 0.007f
-                            || progress >= 0.5f;
-                }
-                mPrevPrevProgressDelta = mPrevProgressDelta;
-                mPrevProgressDelta = progressDelta;
-                int startScroll = mRecentsView.getScrollForPage(
-                        mRecentsView.indexOfChild(currentPage));
-                int scrollDiff = mRecentsView.getScrollForPage(mRecentsView.indexOfChild(nextPage))
-                        - startScroll;
-
-                int linearScrollDiff = (int) (progress * scrollDiff);
-                currentPage.setZoomScale(1 - DEACCEL_3.getInterpolation(progress)
-                        * TaskView.EDGE_SCALE_DOWN_FACTOR);
-                if (!ENABLE_TASK_STABILIZER.get()) {
-                    float accelScrollDiff = ACCEL.getInterpolation(progress) * scrollDiff;
-                    currentPage.setTranslationX(linearScrollDiff + accelScrollDiff);
-                }
-                nextPage.setTranslationZ(1);
-                nextPage.setTranslationY(currentPage.getTranslationY());
-                mRecentsView.setScrollX(startScroll + linearScrollDiff);
-            }
-            return;
-        }
-
-        int quickScrubSection = 0;
-        for (float threshold : QUICK_SCRUB_THRESHOLDS) {
-            if (progress < threshold) {
-                break;
-            }
-            quickScrubSection++;
-        }
-        if (quickScrubSection != mQuickScrubSection) {
-            boolean cameFromAutoAdvance = mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
-                    || mQuickScrubSection == 0;
-            int pageToGoTo = mRecentsView.getNextPage() + quickScrubSection - mQuickScrubSection;
-            if (mFinishedTransitionToQuickScrub && !cameFromAutoAdvance) {
-                goToPageWithHaptic(pageToGoTo);
-            }
-            if (ENABLE_AUTO_ADVANCE) {
-                if (quickScrubSection == QUICK_SCRUB_THRESHOLDS.length || quickScrubSection == 0) {
-                    mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
-                } else {
-                    mAutoAdvanceAlarm.cancelAlarm();
-                }
-            }
-            mQuickScrubSection = quickScrubSection;
-        }
-    }
-
-    public void onFinishedTransitionToQuickScrub() {
-        mFinishedTransitionToQuickScrub = true;
-        Runnable action = mOnFinishedTransitionToQuickScrubRunnable;
-        // Clear the runnable before executing it, to prevent potential recursion.
-        mOnFinishedTransitionToQuickScrubRunnable = null;
-        if (action != null) {
-            action.run();
-        }
-        mRecentsView.setEnableDrawingLiveTile(true);
-    }
-
-    public void onTaskRemoved(int taskId) {
-        if (mLaunchingTaskId == taskId) {
-            // The task has been removed mid-launch, break out of quickscrub and return the user
-            // to where they were before (and notify the launch failed)
-            TaskView taskView = mRecentsView.getTaskView(taskId);
-            if (taskView != null) {
-                taskView.notifyTaskLaunchFailed(TAG);
-            }
-            breakOutOfQuickScrub();
-        }
-    }
-
-    public void snapToNextTaskIfAvailable() {
-        if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
-            int duration = mIsQuickSwitch
-                    ? QUICK_SWITCH_FROM_APP_START_DURATION
-                    : mStartedFromHome
-                        ? QUICK_SCRUB_FROM_HOME_START_DURATION
-                        : QUICK_SCRUB_FROM_APP_START_DURATION;
-            final int pageToGoTo;
-            if (mStartedFromHome) {
-                pageToGoTo = 0;
-            } else if (mIsQuickSwitch) {
-                TaskView tv = mRecentsView.getRunningTaskView();
-                pageToGoTo = tv != null ? mRecentsView.indexOfChild(tv)
-                        : mRecentsView.getNextPage();
-            } else {
-                pageToGoTo = mRecentsView.getNextPage() + 1;
-            }
-            goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
-                    QUICK_SCRUB_START_INTERPOLATOR);
-        }
-    }
-
-    private void goToPageWithHaptic(int pageToGoTo) {
-        goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */, null);
-    }
-
-    private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic,
-            Interpolator interpolator) {
-        pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getTaskViewCount() - 1);
-        boolean snappingToPage = pageToGoTo != mRecentsView.getNextPage();
-        if (snappingToPage) {
-            int duration = overrideDuration > -1 ? overrideDuration
-                    : Math.abs(pageToGoTo - mRecentsView.getNextPage())
-                            * QUICKSCRUB_SNAP_DURATION_PER_PAGE;
-            mRecentsView.snapToPage(pageToGoTo, duration, interpolator);
-        }
-        if (snappingToPage || forceHaptic) {
-            mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
-                    HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
-        }
-    }
-
-    @Override
-    public void onAlarm(Alarm alarm) {
-        int currPage = mRecentsView.getNextPage();
-        boolean recentsVisible = mActivityControlHelper != null
-                && mActivityControlHelper.getVisibleRecentsView() != null;
-        if (!recentsVisible) {
-            Log.w(TAG, "Failed to auto advance; recents not visible");
-            return;
-        }
-        if (mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
-                && currPage < mRecentsView.getTaskViewCount() - 1) {
-            goToPageWithHaptic(currPage + 1);
-        } else if (mQuickScrubSection == 0 && currPage > 0) {
-            goToPageWithHaptic(currPage - 1);
-        }
-        if (ENABLE_AUTO_ADVANCE) {
-            mAutoAdvanceAlarm.setAlarm(AUTO_ADVANCE_DELAY);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java b/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
deleted file mode 100644
index 2c3f77f..0000000
--- a/quickstep/src/com/android/quickstep/QuickstepProcessInitializer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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;
-
-import android.content.Context;
-
-import com.android.launcher3.MainProcessInitializer;
-import com.android.systemui.shared.system.ThreadedRendererCompat;
-
-@SuppressWarnings("unused")
-public class QuickstepProcessInitializer extends MainProcessInitializer {
-
-    public QuickstepProcessInitializer(Context context) { }
-
-    @Override
-    protected void init(Context context) {
-        super.init(context);
-
-        // Elevate GPU priority for Quickstep and Remote animations.
-        ThreadedRendererCompat.setContextPriority(ThreadedRendererCompat.EGL_CONTEXT_PRIORITY_HIGH_IMG);
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index cedd952..e15a3f1 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -60,6 +60,7 @@
         mBgThreadExecutor = BackgroundExecutor.get();
         mKeyguardManager = new KeyguardManagerCompat(context);
         mChangeId = 1;
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
     }
 
     /**
@@ -73,6 +74,14 @@
         });
     }
 
+    public void startStabilizationSession() {
+        mStabilizer.startStabilizationSession();
+    }
+
+    public void endStabilizationSession() {
+        mStabilizer.endStabilizationSession();
+    }
+
     /**
      * Asynchronously fetches the list of recent tasks, reusing cached list if available.
      *
@@ -89,6 +98,7 @@
         if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
             // The list is up to date, callback with the same list
             mMainThreadExecutor.execute(resultCallback);
+            return requestLoadId;
         }
 
         // Kick off task loading in the background
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
index fb6090e..0822e61 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
@@ -30,17 +30,18 @@
 import java.util.function.BiPredicate;
 
 /**
- * Utility class to track create/destroy for RecentsActivity
+ * Utility class to track create/destroy for some {@link BaseRecentsActivity}.
  */
 @TargetApi(Build.VERSION_CODES.P)
-public class RecentsActivityTracker implements ActivityInitListener {
+public class RecentsActivityTracker<T extends BaseRecentsActivity> implements ActivityInitListener {
 
-    private static WeakReference<RecentsActivity> sCurrentActivity = new WeakReference<>(null);
+    private static WeakReference<BaseRecentsActivity> sCurrentActivity =
+            new WeakReference<>(null);
     private static final Scheduler sScheduler = new Scheduler();
 
-    private final BiPredicate<RecentsActivity, Boolean> mOnInitListener;
+    private final BiPredicate<T, Boolean> mOnInitListener;
 
-    public RecentsActivityTracker(BiPredicate<RecentsActivity, Boolean> onInitListener) {
+    public RecentsActivityTracker(BiPredicate<T, Boolean> onInitListener) {
         mOnInitListener = onInitListener;
     }
 
@@ -54,12 +55,12 @@
         sScheduler.clearReference(this);
     }
 
-    private boolean init(RecentsActivity activity, boolean visible) {
+    private boolean init(T activity, boolean visible) {
         return mOnInitListener.test(activity, visible);
     }
 
-    public static RecentsActivity getCurrentActivity() {
-        return sCurrentActivity.get();
+    public static <T extends BaseRecentsActivity> T getCurrentActivity() {
+        return (T) sCurrentActivity.get();
     }
 
     @Override
@@ -71,17 +72,17 @@
         context.startActivity(intent, options);
     }
 
-    public static void onRecentsActivityCreate(RecentsActivity activity) {
+    public static void onRecentsActivityCreate(BaseRecentsActivity activity) {
         sCurrentActivity = new WeakReference<>(activity);
         sScheduler.initIfPending(activity, false);
     }
 
 
-    public static void onRecentsActivityNewIntent(RecentsActivity activity) {
+    public static void onRecentsActivityNewIntent(BaseRecentsActivity activity) {
         sScheduler.initIfPending(activity, activity.isStarted());
     }
 
-    public static void onRecentsActivityDestroy(RecentsActivity activity) {
+    public static void onRecentsActivityDestroy(BaseRecentsActivity activity) {
         if (sCurrentActivity.get() == activity) {
             sCurrentActivity.clear();
         }
@@ -103,13 +104,14 @@
 
         @Override
         public void run() {
-            RecentsActivity activity = sCurrentActivity.get();
+            BaseRecentsActivity activity = sCurrentActivity.get();
             if (activity != null) {
                 initIfPending(activity, activity.isStarted());
             }
         }
 
-        public synchronized boolean initIfPending(RecentsActivity activity, boolean alreadyOnHome) {
+        public synchronized boolean initIfPending(BaseRecentsActivity activity,
+                boolean alreadyOnHome) {
             RecentsActivityTracker tracker = mPendingTracker.get();
             if (tracker != null) {
                 if (!tracker.init(activity, alreadyOnHome)) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java b/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
deleted file mode 100644
index c2d4d80..0000000
--- a/quickstep/src/com/android/quickstep/RecentsAnimationWrapper.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * 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;
-
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.util.LooperExecutor;
-import com.android.launcher3.util.Preconditions;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.util.UiThreadHelper;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-
-import java.util.ArrayList;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Supplier;
-
-import androidx.annotation.UiThread;
-
-/**
- * Wrapper around RecentsAnimationController to help with some synchronization
- */
-public class RecentsAnimationWrapper {
-
-    // A list of callbacks to run when we receive the recents animation target. There are different
-    // than the state callbacks as these run on the current worker thread.
-    private final ArrayList<Runnable> mCallbacks = new ArrayList<>();
-
-    public RemoteAnimationTargetSet targetSet;
-
-    private RecentsAnimationControllerCompat mController;
-    private boolean mInputConsumerEnabled = false;
-    private boolean mBehindSystemBars = true;
-    private boolean mSplitScreenMinimized = false;
-
-    private final ExecutorService mExecutorService =
-            new LooperExecutor(UiThreadHelper.getBackgroundLooper());
-
-    private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
-    private final InputConsumerController mInputConsumer;
-    private final Supplier<TouchConsumer> mTouchProxySupplier;
-
-    private TouchConsumer mTouchConsumer;
-    private boolean mTouchInProgress;
-
-    private boolean mFinishPending;
-
-    public RecentsAnimationWrapper(InputConsumerController inputConsumer,
-            Supplier<TouchConsumer> touchProxySupplier) {
-        mInputConsumer = inputConsumer;
-        mTouchProxySupplier = touchProxySupplier;
-    }
-
-    @UiThread
-    public synchronized void setController(
-            RecentsAnimationControllerCompat controller, RemoteAnimationTargetSet targetSet) {
-        Preconditions.assertUIThread();
-        TraceHelper.partitionSection("RecentsController", "Set controller " + controller);
-        this.mController = controller;
-        this.targetSet = targetSet;
-
-        if (controller == null) {
-            return;
-        }
-        if (mInputConsumerEnabled) {
-            enableInputConsumer();
-        }
-
-        if (!mCallbacks.isEmpty()) {
-            for (Runnable action : new ArrayList<>(mCallbacks)) {
-                action.run();
-            }
-            mCallbacks.clear();
-        }
-    }
-
-    public synchronized void runOnInit(Runnable action) {
-        if (targetSet == null) {
-            mCallbacks.add(action);
-        } else {
-            action.run();
-        }
-    }
-
-    /**
-     * @param onFinishComplete A callback that runs on the main thread after the animation
-     *                         controller has finished on the background thread.
-     */
-    public void finish(boolean toRecents, Runnable onFinishComplete) {
-        if (!toRecents) {
-            mExecutorService.submit(() -> finishBg(false, onFinishComplete));
-            return;
-        }
-
-        mMainThreadExecutor.execute(() -> {
-            if (mTouchInProgress) {
-                mFinishPending = true;
-                // Execute the callback
-                if (onFinishComplete != null) {
-                    onFinishComplete.run();
-                }
-            } else {
-                mExecutorService.submit(() -> finishBg(true, onFinishComplete));
-            }
-        });
-    }
-
-    protected void finishBg(boolean toRecents, Runnable onFinishComplete) {
-        RecentsAnimationControllerCompat controller = mController;
-        mController = null;
-        TraceHelper.endSection("RecentsController", "Finish " + controller
-                + ", toRecents=" + toRecents);
-        if (controller != null) {
-            controller.setInputConsumerEnabled(false);
-            controller.finish(toRecents);
-
-            if (onFinishComplete != null) {
-                mMainThreadExecutor.execute(onFinishComplete);
-            }
-        }
-    }
-
-    public void enableInputConsumer() {
-        mInputConsumerEnabled = true;
-        if (mInputConsumerEnabled) {
-            mExecutorService.submit(() -> {
-                RecentsAnimationControllerCompat controller = mController;
-                TraceHelper.partitionSection("RecentsController",
-                        "Enabling consumer on " + controller);
-                if (controller != null) {
-                    controller.setInputConsumerEnabled(true);
-                }
-            });
-        }
-    }
-
-    public void enableTouchProxy() {
-        mMainThreadExecutor.execute(this::enableTouchProxyUi);
-    }
-
-    private void enableTouchProxyUi() {
-        mInputConsumer.setTouchListener(this::onInputConsumerTouch);
-    }
-
-    private boolean onInputConsumerTouch(MotionEvent ev) {
-        int action = ev.getAction();
-        if (action == ACTION_DOWN) {
-            mTouchInProgress = true;
-            mTouchConsumer = mTouchProxySupplier.get();
-        } else if (action == ACTION_CANCEL || action == ACTION_UP) {
-            // Finish any pending actions
-            mTouchInProgress = false;
-            if (mFinishPending) {
-                mFinishPending = false;
-                mExecutorService.submit(() -> finishBg(true, null));
-            }
-        }
-        if (mTouchConsumer != null) {
-            mTouchConsumer.accept(ev);
-        }
-
-        return true;
-    }
-
-    public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) {
-        if (mBehindSystemBars == behindSystemBars) {
-            return;
-        }
-        mBehindSystemBars = behindSystemBars;
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            TraceHelper.partitionSection("RecentsController",
-                    "Setting behind system bars on " + controller);
-            if (controller != null) {
-                controller.setAnimationTargetsBehindSystemBars(behindSystemBars);
-            }
-        });
-    }
-
-    /**
-     * NOTE: As a workaround for conflicting animations (Launcher animating the task leash, and
-     * SystemUI resizing the docked stack, which resizes the task), we currently only set the
-     * minimized mode, and not the inverse.
-     * TODO: Synchronize the minimize animation with the launcher animation
-     */
-    public void setSplitScreenMinimizedForTransaction(boolean minimized) {
-        if (mSplitScreenMinimized || !minimized) {
-            return;
-        }
-        mSplitScreenMinimized = minimized;
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            TraceHelper.partitionSection("RecentsController",
-                    "Setting minimize dock on " + controller);
-            if (controller != null) {
-                controller.setSplitScreenMinimized(minimized);
-            }
-        });
-    }
-
-    public void hideCurrentInputMethod() {
-        mExecutorService.submit(() -> {
-            RecentsAnimationControllerCompat controller = mController;
-            TraceHelper.partitionSection("RecentsController",
-                    "Hiding currentinput method on " + controller);
-            if (controller != null) {
-                controller.hideCurrentInputMethod();
-            }
-        });
-    }
-
-    public RecentsAnimationControllerCompat getController() {
-        return mController;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index e61c00a..81a22a1 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -22,23 +22,21 @@
 import android.content.ComponentCallbacks2;
 import android.content.Context;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.HandlerThread;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.Log;
-import android.util.SparseArray;
-import com.android.launcher3.MainThreadExecutor;
+
 import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.ISystemUiProxy;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
-import java.util.ArrayList;
-import java.util.function.Consumer;
 
-import androidx.annotation.WorkerThread;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * Singleton class to load and manage recents model.
@@ -52,14 +50,10 @@
     public static final MainThreadInitializedObject<RecentsModel> INSTANCE =
             new MainThreadInitializedObject<>(c -> new RecentsModel(c));
 
-    private final SparseArray<Bundle> mCachedAssistData = new SparseArray<>(1);
-    private final ArrayList<AssistDataListener> mAssistDataListeners = new ArrayList<>();
-
+    private final List<TaskThumbnailChangeListener> mThumbnailChangeListeners = new ArrayList<>();
     private final Context mContext;
-    private final MainThreadExecutor mMainThreadExecutor;
 
     private ISystemUiProxy mSystemUiProxy;
-    private boolean mClearAssistCacheOnStackChange = true;
 
     private final RecentTasksList mTaskList;
     private final TaskIconCache mIconCache;
@@ -70,9 +64,6 @@
 
     private RecentsModel(Context context) {
         mContext = context;
-
-        mMainThreadExecutor = new MainThreadExecutor();
-
         HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
                 Process.THREAD_PRIORITY_BACKGROUND);
         loaderThread.start();
@@ -90,6 +81,14 @@
         return mThumbnailCache;
     }
 
+    public void startStabilizationSession() {
+        mTaskList.startStabilizationSession();
+    }
+
+    public void endStabilizationSession() {
+        mTaskList.endStabilizationSession();
+    }
+
     /**
      * Fetches the list of recent tasks.
      *
@@ -163,12 +162,14 @@
     }
 
     @Override
-    public void onTaskStackChanged() {
-        Preconditions.assertUIThread();
-        if (mClearAssistCacheOnStackChange) {
-            mCachedAssistData.clear();
-        } else {
-            mClearAssistCacheOnStackChange = true;
+    public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
+        mThumbnailCache.updateTaskSnapShot(taskId, snapshot);
+
+        for (int i = mThumbnailChangeListeners.size() - 1; i >= 0; i--) {
+            Task task = mThumbnailChangeListeners.get(i).onTaskThumbnailChanged(taskId, snapshot);
+            if (task != null) {
+                task.thumbnail = snapshot;
+            }
         }
     }
 
@@ -244,43 +245,16 @@
         }
     }
 
-    public void resetAssistCache() {
-        mCachedAssistData.clear();
+    public void addThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+        mThumbnailChangeListeners.add(listener);
     }
 
-    @WorkerThread
-    public void preloadAssistData(int taskId, Bundle data) {
-        mMainThreadExecutor.execute(() -> {
-            mCachedAssistData.put(taskId, data);
-            // We expect a stack change callback after the assist data is set. So ignore the
-            // very next stack change callback.
-            mClearAssistCacheOnStackChange = false;
-
-            int count = mAssistDataListeners.size();
-            for (int i = 0; i < count; i++) {
-                mAssistDataListeners.get(i).onAssistDataReceived(taskId);
-            }
-        });
+    public void removeThumbnailChangeListener(TaskThumbnailChangeListener listener) {
+        mThumbnailChangeListeners.remove(listener);
     }
 
-    public Bundle getAssistData(int taskId) {
-        Preconditions.assertUIThread();
-        return mCachedAssistData.get(taskId);
-    }
+    public interface TaskThumbnailChangeListener {
 
-    public void addAssistDataListener(AssistDataListener listener) {
-        mAssistDataListeners.add(listener);
-    }
-
-    public void removeAssistDataListener(AssistDataListener listener) {
-        mAssistDataListeners.remove(listener);
-    }
-
-    /**
-     * Callback for receiving assist data
-     */
-    public interface AssistDataListener {
-
-        void onAssistDataReceived(int taskId);
+        Task onTaskThumbnailChanged(int taskId, ThumbnailData thumbnailData);
     }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskListStabilizer.java b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
index 3eb26b4..4c63f81 100644
--- a/quickstep/src/com/android/quickstep/TaskListStabilizer.java
+++ b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
@@ -15,13 +15,10 @@
  */
 package com.android.quickstep;
 
-import static com.android.launcher3.config.FeatureFlags.ENABLE_TASK_STABILIZER;
-
 import android.app.ActivityManager.RecentTaskInfo;
 import android.content.ComponentName;
 import android.os.Process;
 import android.os.SystemClock;
-import android.util.Log;
 
 import com.android.launcher3.util.IntArray;
 import com.android.systemui.shared.recents.model.Task;
@@ -33,98 +30,77 @@
 import java.util.Collections;
 import java.util.List;
 
+/**
+ * Keeps the task list stable during quick switch gestures. So if you swipe right to switch from app
+ * A to B, you can then swipe right again to get to app C or left to get back to A.
+ */
 public class TaskListStabilizer {
 
     private static final long TASK_CACHE_TIMEOUT_MS = 5000;
 
-    private static final int INVALID_TASK_ID = -1;
-
     private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
 
         @Override
         public void onTaskCreated(int taskId, ComponentName componentName) {
-            onTaskCreatedInternal(taskId);
-        }
-
-        @Override
-        public void onTaskMovedToFront(int taskId) {
-            onTaskMovedToFrontInternal(taskId);
+            endStabilizationSession();
         }
 
         @Override
         public void onTaskRemoved(int taskId) {
-            onTaskRemovedInternal(taskId);
+            endStabilizationSession();
         }
     };
 
-    // Task ids ordered based on recency, 0th index is the latest task
-    private final IntArray mOrderedTaskIds;
+    // Task ids ordered based on recency, 0th index is the least recent task
+    private final IntArray mSystemOrder;
+    private final IntArray mStabilizedOrder;
 
     // Wrapper objects used for sorting tasks
     private final ArrayList<TaskWrapper> mTaskWrappers = new ArrayList<>();
 
-    // Information about recent task re-order which has not been applied yet
-    private int mScheduledMoveTaskId = INVALID_TASK_ID;
-    private long mScheduledMoveTime = 0;
+    private boolean mInStabilizationSession;
+    private long mSessionStartTime;
 
     public TaskListStabilizer() {
-        if (ENABLE_TASK_STABILIZER.get()) {
-            // Initialize the task ids map
-            List<RecentTaskInfo> rawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
-                    Integer.MAX_VALUE, Process.myUserHandle().getIdentifier());
-            mOrderedTaskIds = new IntArray(rawTasks.size());
-            for (RecentTaskInfo info : rawTasks) {
-                mOrderedTaskIds.add(new TaskKey(info).id);
-            }
-
-            ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
-        } else {
-            mOrderedTaskIds = null;
+        // Initialize the task ids map
+        List<RecentTaskInfo> rawTasks = ActivityManagerWrapper.getInstance().getRecentTasks(
+                Integer.MAX_VALUE, Process.myUserHandle().getIdentifier());
+        mSystemOrder = new IntArray(rawTasks.size());
+        for (RecentTaskInfo info : rawTasks) {
+            mSystemOrder.add(new TaskKey(info).id);
         }
+        // We will lazily copy the task id's from mSystemOrder when a stabilization session starts.
+        mStabilizedOrder = new IntArray(rawTasks.size());
+
+        ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
     }
 
-    private synchronized void onTaskCreatedInternal(int taskId) {
-        applyScheduledMoveUnchecked();
-        mOrderedTaskIds.add(taskId);
-    }
-
-    private synchronized void onTaskRemovedInternal(int taskId) {
-        applyScheduledMoveUnchecked();
-        mOrderedTaskIds.removeValue(taskId);
-    }
-
-    private void applyScheduledMoveUnchecked() {
-        if (mScheduledMoveTaskId != INVALID_TASK_ID) {
-            // Mode the scheduled task to front
-            mOrderedTaskIds.removeValue(mScheduledMoveTaskId);
-            mOrderedTaskIds.add(mScheduledMoveTaskId);
-            mScheduledMoveTaskId = INVALID_TASK_ID;
+    public synchronized void startStabilizationSession() {
+        if (!mInStabilizationSession) {
+            mStabilizedOrder.clear();
+            mStabilizedOrder.addAll(mSystemOrder);
         }
+        mInStabilizationSession = true;
+        mSessionStartTime = SystemClock.uptimeMillis();
     }
 
-    /**
-     * Checks if the scheduled move has timed out and moves the task to front accordingly.
-     */
-    private void applyScheduledMoveIfTime() {
-        if (mScheduledMoveTaskId != INVALID_TASK_ID
-                && (SystemClock.uptimeMillis() - mScheduledMoveTime) > TASK_CACHE_TIMEOUT_MS) {
-            applyScheduledMoveUnchecked();
-        }
+    public synchronized void endStabilizationSession() {
+        mInStabilizationSession = false;
     }
 
-    private synchronized void onTaskMovedToFrontInternal(int taskId) {
-        applyScheduledMoveIfTime();
-        mScheduledMoveTaskId = taskId;
-        mScheduledMoveTime = SystemClock.uptimeMillis();
-    }
-
-
     public synchronized ArrayList<Task> reorder(ArrayList<Task> tasks) {
-        if (!ENABLE_TASK_STABILIZER.get()) {
-            return tasks;
+        mSystemOrder.clear();
+        for (Task task : tasks) {
+            mSystemOrder.add(task.key.id);
         }
 
-        applyScheduledMoveIfTime();
+        if ((SystemClock.uptimeMillis() - mSessionStartTime) > TASK_CACHE_TIMEOUT_MS) {
+            endStabilizationSession();
+        }
+
+        if (!mInStabilizationSession) {
+            return tasks;
+        }
 
         // Ensure that we have enough wrappers
         int taskCount = tasks.size();
@@ -139,7 +115,7 @@
         for (int i = 0; i < taskCount; i++){
             TaskWrapper wrapper = listToSort.get(i);
             wrapper.task = tasks.get(i);
-            wrapper.index = mOrderedTaskIds.indexOf(wrapper.task.key.id);
+            wrapper.index = mStabilizedOrder.indexOf(wrapper.task.key.id);
 
             // Ensure that missing tasks are put in the front, in the order they appear in the
             // original list
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
index 7a216ed..d05196b 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailCache.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.icons.cache.HandlerRunnable;
 import com.android.launcher3.util.Preconditions;
 import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.recents.model.Task.TaskKey;
 import com.android.systemui.shared.recents.model.TaskKeyLruCache;
 import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -38,7 +39,7 @@
     private final MainThreadExecutor mMainThreadExecutor;
 
     private final int mCacheSize;
-    private final TaskKeyLruCache<ThumbnailData> mCache;
+    private final ThumbnailCache mCache;
     private final HighResLoadingState mHighResLoadingState;
 
     public static class HighResLoadingState {
@@ -98,7 +99,7 @@
 
         Resources res = context.getResources();
         mCacheSize = res.getInteger(R.integer.recentsThumbnailCacheSize);
-        mCache = new TaskKeyLruCache<>(mCacheSize);
+        mCache = new ThumbnailCache(mCacheSize);
     }
 
     /**
@@ -106,13 +107,20 @@
      */
     public void updateThumbnailInCache(Task task) {
         Preconditions.assertUIThread();
-
         // Fetch the thumbnail for this task and put it in the cache
-        updateThumbnailInBackground(task, true /* reducedResolution */, (t) -> {
-            mCache.put(task.key, t.thumbnail);
-        });
+        if (task.thumbnail == null) {
+            updateThumbnailInBackground(task.key, true /* reducedResolution */,
+                    t -> task.thumbnail = t);
+        }
     }
 
+    /**
+     * Synchronously updates the thumbnail in the cache if it is already there.
+     */
+    public void updateTaskSnapShot(int taskId, ThumbnailData thumbnail) {
+        Preconditions.assertUIThread();
+        mCache.updateIfAlreadyInCache(taskId, thumbnail);
+    }
 
     /**
      * Asynchronously fetches the icon and other task data for the given {@param task}.
@@ -120,22 +128,33 @@
      * @param callback The callback to receive the task after its data has been populated.
      * @return A cancelable handle to the request
      */
-    public ThumbnailLoadRequest updateThumbnailInBackground(Task task, boolean reducedResolution,
-            Consumer<Task> callback) {
+    public ThumbnailLoadRequest updateThumbnailInBackground(
+            Task task, Consumer<ThumbnailData> callback) {
         Preconditions.assertUIThread();
 
+        boolean reducedResolution = !mHighResLoadingState.isEnabled();
         if (task.thumbnail != null && (!task.thumbnail.reducedResolution || reducedResolution)) {
             // Nothing to load, the thumbnail is already high-resolution or matches what the
             // request, so just callback
-            callback.accept(task);
+            callback.accept(task.thumbnail);
             return null;
         }
 
-        ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(task.key);
+
+        return updateThumbnailInBackground(task.key, !mHighResLoadingState.isEnabled(), t -> {
+            task.thumbnail = t;
+            callback.accept(t);
+        });
+    }
+
+    private ThumbnailLoadRequest updateThumbnailInBackground(TaskKey key, boolean reducedResolution,
+            Consumer<ThumbnailData> callback) {
+        Preconditions.assertUIThread();
+
+        ThumbnailData cachedThumbnail = mCache.getAndInvalidateIfModified(key);
         if (cachedThumbnail != null && (!cachedThumbnail.reducedResolution || reducedResolution)) {
             // Already cached, lets use that thumbnail
-            task.thumbnail = cachedThumbnail;
-            callback.accept(task);
+            callback.accept(cachedThumbnail);
             return null;
         }
 
@@ -144,14 +163,14 @@
             @Override
             public void run() {
                 ThumbnailData thumbnail = ActivityManagerWrapper.getInstance().getTaskThumbnail(
-                        task.key.id, reducedResolution);
+                        key.id, reducedResolution);
                 if (isCanceled()) {
                     // We don't call back to the provided callback in this case
                     return;
                 }
                 mMainThreadExecutor.execute(() -> {
-                    task.thumbnail = thumbnail;
-                    callback.accept(task);
+                    mCache.put(key, thumbnail);
+                    callback.accept(thumbnail);
                     onEnd();
                 });
             }
@@ -196,4 +215,21 @@
             this.reducedResolution = reducedResolution;
         }
     }
+
+    private static class ThumbnailCache extends TaskKeyLruCache<ThumbnailData> {
+
+        public ThumbnailCache(int cacheSize) {
+            super(cacheSize);
+        }
+
+        /**
+         * Updates the cache entry if it is already present in the cache
+         */
+        public void updateIfAlreadyInCache(int taskId, ThumbnailData thumbnailData) {
+            ThumbnailData oldData = getCacheEntry(taskId);
+            if (oldData != null) {
+                putCacheEntry(taskId, thumbnailData);
+            }
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index c2777e7..5f76ca7 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,44 +16,30 @@
 
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.graphics.RectF;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.View;
 
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 
 import java.util.List;
 
 /**
  * Contains helpful methods for retrieving data from {@link Task}s.
  */
-public class TaskUtils {
+public final class TaskUtils {
 
     private static final String TAG = "TaskUtils";
 
+    private TaskUtils() {}
+
     /**
      * TODO: remove this once we switch to getting the icon and label from IconCache.
      */
@@ -79,117 +65,6 @@
     }
 
 
-    /**
-     * Try to find a TaskView that corresponds with the component of the launched view.
-     *
-     * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
-     * Otherwise, we will assume we are using a normal app transition, but it's possible that the
-     * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
-     */
-    public static TaskView findTaskViewToLaunch(
-            BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
-        RecentsView recentsView = activity.getOverviewPanel();
-        if (v instanceof TaskView) {
-            TaskView taskView = (TaskView) v;
-            return recentsView.isTaskViewVisible(taskView) ? taskView : null;
-        }
-
-        // It's possible that the launched view can still be resolved to a visible task view, check
-        // the task id of the opening task and see if we can find a match.
-        if (v.getTag() instanceof ItemInfo) {
-            ItemInfo itemInfo = (ItemInfo) v.getTag();
-            ComponentName componentName = itemInfo.getTargetComponent();
-            int userId = itemInfo.user.getIdentifier();
-            if (componentName != null) {
-                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
-                    TaskView taskView = recentsView.getTaskViewAt(i);
-                    if (recentsView.isTaskViewVisible(taskView)) {
-                        Task.TaskKey key = taskView.getTask().key;
-                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
-                            return taskView;
-                        }
-                    }
-                }
-            }
-        }
-
-        if (targets == null) {
-            return null;
-        }
-        // Resolve the opening task id
-        int openingTaskId = -1;
-        for (RemoteAnimationTargetCompat target : targets) {
-            if (target.mode == MODE_OPENING) {
-                openingTaskId = target.taskId;
-                break;
-            }
-        }
-
-        // If there is no opening task id, fall back to the normal app icon launch animation
-        if (openingTaskId == -1) {
-            return null;
-        }
-
-        // If the opening task id is not currently visible in overview, then fall back to normal app
-        // icon launch animation
-        TaskView taskView = recentsView.getTaskView(openingTaskId);
-        if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
-            return null;
-        }
-        return taskView;
-    }
-
-    /**
-     * @return Animator that controls the window of the opening targets for the recents launch
-     * animation.
-     */
-    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
-        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
-                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
-        
-        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
-        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
-
-            // Defer fading out the view until after the app window gets faded in
-            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
-            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
-
-            final RemoteAnimationTargetSet mTargetSet;
-
-            final RectF mThumbnailRect;
-
-            {
-                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
-                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
-
-                inOutHelper.prepareAnimation(true /* isOpening */);
-                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
-                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
-
-                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
-                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
-                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
-            }
-
-            @Override
-            public void onUpdate(float percent) {
-                params.setProgress(1 - percent);
-                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, params);
-                if (!skipViewChanges) {
-                    float scale = taskBounds.width() / mThumbnailRect.width();
-                    v.setScaleX(scale);
-                    v.setScaleY(scale);
-                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
-                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
-                    v.setAlpha(mViewAlpha.value);
-                }
-            }
-        });
-        return appAnimator;
-    }
-
     public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
             int taskId, int mode) {
         for (RemoteAnimationTargetCompat target : targets) {
diff --git a/quickstep/src/com/android/quickstep/TouchConsumer.java b/quickstep/src/com/android/quickstep/TouchConsumer.java
deleted file mode 100644
index 026f715..0000000
--- a/quickstep/src/com/android/quickstep/TouchConsumer.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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;
-
-import android.annotation.TargetApi;
-import android.os.Build;
-import android.view.MotionEvent;
-
-import androidx.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.function.Consumer;
-
-@TargetApi(Build.VERSION_CODES.O)
-@FunctionalInterface
-public interface TouchConsumer extends Consumer<MotionEvent> {
-
-    TouchConsumer NO_OP = (ev) -> {};
-
-    @IntDef(flag = true, value = {
-            INTERACTION_NORMAL,
-            INTERACTION_QUICK_SCRUB
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    @interface InteractionType {}
-    int INTERACTION_NORMAL = 0;
-    int INTERACTION_QUICK_SCRUB = 1;
-
-    default void onQuickScrubStart() { }
-
-    default void onQuickScrubEnd() { }
-
-    default void onQuickScrubProgress(float progress) { }
-
-    default void onQuickStep(MotionEvent ev) { }
-
-    default void onShowOverviewFromAltTab() {}
-
-    default boolean isActive() {
-        return false;
-    }
-
-    /**
-     * Called by the event queue when the consumer is about to be switched to a new consumer.
-     */
-    default void onConsumerAboutToBeSwitched() { }
-}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
deleted file mode 100644
index 951ba4f..0000000
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ /dev/null
@@ -1,463 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_MOVE;
-import static android.view.MotionEvent.ACTION_POINTER_DOWN;
-import static android.view.MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
-import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
-
-import android.annotation.TargetApi;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.Service;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.graphics.Region;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.Looper;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Choreographer;
-import android.view.MotionEvent;
-import android.view.ViewConfiguration;
-
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.util.TraceHelper;
-import com.android.launcher3.views.BaseDragLayer;
-import com.android.quickstep.views.RecentsView;
-import com.android.systemui.shared.recents.IOverviewProxy;
-import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.NavigationBarCompat.HitTarget;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Service connected by system-UI for handling touch interaction.
- */
-@TargetApi(Build.VERSION_CODES.O)
-public class TouchInteractionService extends Service {
-
-    public static final MainThreadExecutor MAIN_THREAD_EXECUTOR = new MainThreadExecutor();
-
-    private static final SparseArray<String> sMotionEventNames;
-
-    static {
-        sMotionEventNames = new SparseArray<>(3);
-        sMotionEventNames.put(ACTION_DOWN, "ACTION_DOWN");
-        sMotionEventNames.put(ACTION_UP, "ACTION_UP");
-        sMotionEventNames.put(ACTION_CANCEL, "ACTION_CANCEL");
-    }
-
-    public static final int EDGE_NAV_BAR = 1 << 8;
-
-    private static final String TAG = "TouchInteractionService";
-
-    private final IBinder mMyBinder = new IOverviewProxy.Stub() {
-
-        public void onActiveNavBarRegionChanges(Region region) { }
-
-        public void onInitialize(Bundle params) { }
-
-        public void onPreMotionEvent(@HitTarget int downHitTarget) {
-            mTouchInteractionLog.prepareForNewGesture();
-
-            TraceHelper.beginSection("SysUiBinder");
-            mEventQueue.onNewGesture(downHitTarget);
-            TraceHelper.partitionSection("SysUiBinder", "Down target " + downHitTarget);
-        }
-
-        public void onMotionEvent(MotionEvent ev) {
-            mEventQueue.queue(ev);
-
-            int action = ev.getActionMasked();
-            if (action == ACTION_DOWN) {
-                mOverviewInteractionState.setSwipeGestureInitializing(true);
-            } else if (action == ACTION_UP || action == ACTION_CANCEL) {
-                mOverviewInteractionState.setSwipeGestureInitializing(false);
-            }
-
-            String name = sMotionEventNames.get(action);
-            if (name != null){
-                TraceHelper.partitionSection("SysUiBinder", name);
-            }
-        }
-
-        public void onBind(ISystemUiProxy iSystemUiProxy) {
-            mISystemUiProxy = iSystemUiProxy;
-            mRecentsModel.setSystemUiProxy(mISystemUiProxy);
-            mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
-        }
-
-        public void onQuickScrubStart() {
-            mEventQueue.onQuickScrubStart();
-            mOverviewInteractionState.setSwipeGestureInitializing(false);
-            TraceHelper.partitionSection("SysUiBinder", "onQuickScrubStart");
-        }
-
-        public void onQuickScrubProgress(float progress) {
-            mEventQueue.onQuickScrubProgress(progress);
-        }
-
-        public void onQuickScrubEnd() {
-            mEventQueue.onQuickScrubEnd();
-            TraceHelper.endSection("SysUiBinder", "onQuickScrubEnd");
-        }
-
-        @Override
-        public void onOverviewToggle() {
-            mOverviewCommandHelper.onOverviewToggle();
-        }
-
-        @Override
-        public void onOverviewShown(boolean triggeredFromAltTab) {
-            if (triggeredFromAltTab) {
-                mEventQueue.onNewGesture(HIT_TARGET_NONE);
-                mEventQueue.onOverviewShownFromAltTab();
-            } else {
-                mOverviewCommandHelper.onOverviewShown();
-            }
-        }
-
-        @Override
-        public void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) {
-            if (triggeredFromAltTab && !triggeredFromHomeKey) {
-                // onOverviewShownFromAltTab initiates quick scrub. Ending it here.
-                mEventQueue.onQuickScrubEnd();
-            }
-        }
-
-        public void onQuickStep(MotionEvent motionEvent) {
-            mEventQueue.onQuickStep(motionEvent);
-            mOverviewInteractionState.setSwipeGestureInitializing(false);
-            TraceHelper.endSection("SysUiBinder", "onQuickStep");
-        }
-
-        @Override
-        public void onTip(int actionType, int viewType) {
-            mOverviewCommandHelper.onTip(actionType, viewType);
-        }
-    };
-
-    private static boolean sConnected = false;
-
-    public static boolean isConnected() {
-        return sConnected;
-    }
-
-    private ActivityManagerWrapper mAM;
-    private RecentsModel mRecentsModel;
-    private MotionEventQueue mEventQueue;
-    private ISystemUiProxy mISystemUiProxy;
-    private OverviewCommandHelper mOverviewCommandHelper;
-    private OverviewComponentObserver mOverviewComponentObserver;
-    private OverviewInteractionState mOverviewInteractionState;
-    private OverviewCallbacks mOverviewCallbacks;
-    private TaskOverlayFactory mTaskOverlayFactory;
-    private TouchInteractionLog mTouchInteractionLog;
-    private InputConsumerController mInputConsumer;
-    private SwipeSharedState mSwipeSharedState;
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        mAM = ActivityManagerWrapper.getInstance();
-        mRecentsModel = RecentsModel.INSTANCE.get(this);
-        mOverviewComponentObserver = new OverviewComponentObserver(this);
-        mOverviewCommandHelper = new OverviewCommandHelper(this, mOverviewComponentObserver);
-        mEventQueue = new MotionEventQueue(Looper.myLooper(), Choreographer.getInstance(),
-                this::newConsumer);
-        mOverviewInteractionState = OverviewInteractionState.INSTANCE.get(this);
-        mOverviewCallbacks = OverviewCallbacks.get(this);
-        mTaskOverlayFactory = TaskOverlayFactory.INSTANCE.get(this);
-        mTouchInteractionLog = new TouchInteractionLog();
-        mSwipeSharedState = new SwipeSharedState();
-        mInputConsumer = InputConsumerController.getRecentsAnimationInputConsumer();
-        mInputConsumer.registerInputConsumer();
-
-        sConnected = true;
-
-        // Temporarily disable model preload
-        // new ModelPreload().start(this);
-    }
-
-    @Override
-    public void onDestroy() {
-        mInputConsumer.unregisterInputConsumer();
-        mOverviewComponentObserver.onDestroy();
-        mEventQueue.dispose();
-        sConnected = false;
-        super.onDestroy();
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        Log.d(TAG, "Touch service connected");
-        return mMyBinder;
-    }
-
-    private TouchConsumer newConsumer(@HitTarget int downHitTarget, boolean useSharedState) {
-        RunningTaskInfo runningTaskInfo = mAM.getRunningTask(0);
-        if (!useSharedState) {
-            mSwipeSharedState.clearAllState();
-        }
-
-        if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
-            return TouchConsumer.NO_OP;
-        } else if (mSwipeSharedState.goingToLauncher ||
-                mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
-            return OverviewTouchConsumer.newInstance(
-                    mOverviewComponentObserver.getActivityControlHelper(), false,
-                    mTouchInteractionLog);
-        } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
-                mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
-            return OverviewTouchConsumer.newInstance(
-                    mOverviewComponentObserver.getActivityControlHelper(), false,
-                    mTouchInteractionLog, false /* waitForWindowAvailable */);
-        } else {
-            return new OtherActivityTouchConsumer(this, runningTaskInfo, mRecentsModel,
-                    mOverviewComponentObserver.getOverviewIntent(),
-                    mOverviewComponentObserver.getActivityControlHelper(),
-                    downHitTarget, mOverviewCallbacks,
-                    mTaskOverlayFactory, mInputConsumer, mTouchInteractionLog, mEventQueue,
-                    mSwipeSharedState);
-        }
-    }
-
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        mTouchInteractionLog.dump(pw);
-    }
-
-    public static class OverviewTouchConsumer<T extends BaseDraggingActivity>
-            implements TouchConsumer {
-
-        private final ActivityControlHelper<T> mActivityHelper;
-        private final T mActivity;
-        private final BaseDragLayer mTarget;
-        private final int[] mLocationOnScreen = new int[2];
-        private final PointF mDownPos = new PointF();
-        private final int mTouchSlop;
-        private final QuickScrubController mQuickScrubController;
-        private final TouchInteractionLog mTouchInteractionLog;
-
-        private final boolean mStartingInActivityBounds;
-
-        private boolean mTrackingStarted = false;
-        private boolean mInvalidated = false;
-
-        private float mLastProgress = 0;
-        private boolean mStartPending = false;
-        private boolean mEndPending = false;
-        private boolean mWaitForWindowAvailable;
-
-        OverviewTouchConsumer(ActivityControlHelper<T> activityHelper, T activity,
-                boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog,
-                boolean waitForWindowAvailable) {
-            mActivityHelper = activityHelper;
-            mActivity = activity;
-            mTarget = activity.getDragLayer();
-            mTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
-            mStartingInActivityBounds = startingInActivityBounds;
-
-            mQuickScrubController = mActivity.<RecentsView>getOverviewPanel()
-                    .getQuickScrubController();
-            mTouchInteractionLog = touchInteractionLog;
-            mTouchInteractionLog.setTouchConsumer(this);
-
-            mWaitForWindowAvailable = waitForWindowAvailable;
-        }
-
-        @Override
-        public void accept(MotionEvent ev) {
-            if (mInvalidated) {
-                return;
-            }
-            mTouchInteractionLog.addMotionEvent(ev);
-            int action = ev.getActionMasked();
-            if (action == ACTION_DOWN) {
-                if (mStartingInActivityBounds) {
-                    startTouchTracking(ev, false /* updateLocationOffset */);
-                    return;
-                }
-                mTrackingStarted = false;
-                mDownPos.set(ev.getX(), ev.getY());
-            } else if (!mTrackingStarted) {
-                switch (action) {
-                    case ACTION_CANCEL:
-                    case ACTION_UP:
-                        startTouchTracking(ev, true /* updateLocationOffset */);
-                        break;
-                    case ACTION_MOVE: {
-                        float displacement = mActivity.getDeviceProfile().isLandscape ?
-                                ev.getX() - mDownPos.x : ev.getY() - mDownPos.y;
-                        if (Math.abs(displacement) >= mTouchSlop) {
-                            // Start tracking only when mTouchSlop is crossed.
-                            startTouchTracking(ev, true /* updateLocationOffset */);
-                        }
-                    }
-                }
-            }
-
-            if (mTrackingStarted) {
-                sendEvent(ev);
-            }
-
-            if (action == ACTION_UP || action == ACTION_CANCEL) {
-                mInvalidated = true;
-            }
-        }
-
-        private void startTouchTracking(MotionEvent ev, boolean updateLocationOffset) {
-            if (updateLocationOffset) {
-                mTarget.getLocationOnScreen(mLocationOnScreen);
-            }
-
-            // Send down touch event
-            MotionEvent down = MotionEvent.obtainNoHistory(ev);
-            down.setAction(ACTION_DOWN);
-            sendEvent(down);
-
-            mTrackingStarted = true;
-            // Send pointer down for remaining pointers.
-            int pointerCount = ev.getPointerCount();
-            for (int i = 1; i < pointerCount; i++) {
-                down.setAction(ACTION_POINTER_DOWN | (i << ACTION_POINTER_INDEX_SHIFT));
-                sendEvent(down);
-            }
-
-            down.recycle();
-        }
-
-        private void sendEvent(MotionEvent ev) {
-            if (!mTarget.verifyTouchDispatch(this, ev)) {
-                mInvalidated = true;
-                return;
-            }
-            int flags = ev.getEdgeFlags();
-            ev.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
-            ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
-            if (!mTrackingStarted) {
-                mTarget.onInterceptTouchEvent(ev);
-            }
-            mTarget.onTouchEvent(ev);
-            ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
-            ev.setEdgeFlags(flags);
-        }
-
-        @Override
-        public void onQuickStep(MotionEvent ev) {
-            if (mInvalidated) {
-                return;
-            }
-            OverviewCallbacks.get(mActivity).closeAllWindows();
-            ActivityManagerWrapper.getInstance()
-                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-            mTouchInteractionLog.startQuickStep();
-        }
-
-        @Override
-        public void onQuickScrubStart() {
-            if (mInvalidated) {
-                return;
-            }
-            mTouchInteractionLog.startQuickScrub();
-            if (!mQuickScrubController.prepareQuickScrub(TAG)) {
-                mInvalidated = true;
-                mTouchInteractionLog.endQuickScrub("onQuickScrubStart");
-                return;
-            }
-            OverviewCallbacks.get(mActivity).closeAllWindows();
-            ActivityManagerWrapper.getInstance()
-                    .closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
-
-            mStartPending = true;
-            Runnable action = () -> {
-                if (!mQuickScrubController.prepareQuickScrub(TAG)) {
-                    mInvalidated = true;
-                    mTouchInteractionLog.endQuickScrub("onQuickScrubStart");
-                    return;
-                }
-                mActivityHelper.onQuickInteractionStart(mActivity, null, true,
-                        mTouchInteractionLog);
-                mQuickScrubController.onQuickScrubProgress(mLastProgress);
-                mStartPending = false;
-
-                if (mEndPending) {
-                    mQuickScrubController.onQuickScrubEnd();
-                    mEndPending = false;
-                }
-            };
-
-            if (mWaitForWindowAvailable) {
-                mActivityHelper.executeOnWindowAvailable(mActivity, action);
-            } else {
-                action.run();
-            }
-        }
-
-        @Override
-        public void onQuickScrubEnd() {
-            mTouchInteractionLog.endQuickScrub("onQuickScrubEnd");
-            if (mInvalidated) {
-                return;
-            }
-            if (mStartPending) {
-                mEndPending = true;
-            } else {
-                mQuickScrubController.onQuickScrubEnd();
-            }
-        }
-
-        @Override
-        public void onQuickScrubProgress(float progress) {
-            mTouchInteractionLog.setQuickScrubProgress(progress);
-            mLastProgress = progress;
-            if (mInvalidated || mStartPending) {
-                return;
-            }
-            mQuickScrubController.onQuickScrubProgress(progress);
-        }
-
-        public static TouchConsumer newInstance(ActivityControlHelper activityHelper,
-                boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog) {
-            return newInstance(activityHelper, startingInActivityBounds, touchInteractionLog,
-                    true /* waitForWindowAvailable */);
-        }
-
-        public static TouchConsumer newInstance(ActivityControlHelper activityHelper,
-                boolean startingInActivityBounds, TouchInteractionLog touchInteractionLog,
-                boolean waitForWindowAvailable) {
-            BaseDraggingActivity activity = activityHelper.getCreatedActivity();
-            if (activity == null) {
-                return TouchConsumer.NO_OP;
-            }
-            return new OverviewTouchConsumer(activityHelper, activity, startingInActivityBounds,
-                    touchInteractionLog, waitForWindowAvailable);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/BinderTracker.java b/quickstep/src/com/android/quickstep/util/BinderTracker.java
new file mode 100644
index 0000000..32d0d53
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/BinderTracker.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util;
+
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Looper;
+import android.util.Log;
+
+import com.android.launcher3.config.FeatureFlags;
+
+/**
+ * Utility class to test and check binder calls during development.
+ */
+public class BinderTracker {
+
+    private static final String TAG = "BinderTracker";
+
+    public static void start() {
+        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+            Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
+            return;
+        }
+
+        Binder.setProxyTransactListener(new Tracker());
+    }
+
+    public static void stop() {
+        if (!FeatureFlags.IS_DOGFOOD_BUILD) {
+            Log.wtf(TAG, "Accessing tracker in released code.", new Exception());
+            return;
+        }
+        Binder.setProxyTransactListener(null);
+    }
+
+    private static class Tracker implements Binder.ProxyTransactListener {
+
+        @Override
+        public Object onTransactStarted(IBinder iBinder, int code) {
+            if (Looper.myLooper() == Looper.getMainLooper()) {
+                Log.e(TAG, "Binder call on ui thread", new Exception());
+            }
+            return null;
+        }
+
+        @Override
+        public void onTransactEnded(Object session) { }
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java b/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
new file mode 100644
index 0000000..194c7d4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/CachedEventDispatcher.java
@@ -0,0 +1,69 @@
+/*
+ * 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.util;
+
+import static com.android.systemui.shared.system.InputChannelCompat.mergeMotionEvent;
+
+import android.view.MotionEvent;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Utility class to dispatch touch events to a different class. It stores the events locally
+ * until a valid dispatcher is available.
+ */
+public class CachedEventDispatcher {
+
+    private Consumer<MotionEvent> mConsumer;
+
+    private ArrayList<MotionEvent> mCache;
+    private MotionEvent mLastEvent;
+
+    public void dispatchEvent(MotionEvent event) {
+        if (mConsumer != null) {
+            mConsumer.accept(event);
+        } else {
+            if (mLastEvent == null || !mergeMotionEvent(event, mLastEvent)) {
+                // Queue event.
+                if (mCache == null) {
+                    mCache = new ArrayList<>();
+                }
+                mLastEvent = MotionEvent.obtain(event);
+                mCache.add(mLastEvent);
+            }
+        }
+    }
+
+    public void setConsumer(Consumer<MotionEvent> consumer) {
+        if (consumer == null) {
+            return;
+        }
+        mConsumer = consumer;
+        int cacheCount = mCache == null ? 0 : mCache.size();
+        for (int i = 0; i < cacheCount; i++) {
+            MotionEvent ev = mCache.get(i);
+            mConsumer.accept(ev);
+            ev.recycle();
+        }
+        mCache = null;
+        mLastEvent = null;
+    }
+
+    public boolean hasConsumer() {
+        return mConsumer != null;
+    }
+}
diff --git a/quickstep/src/com/android/quickstep/util/LayoutUtils.java b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
index ed585c1..a06209a 100644
--- a/quickstep/src/com/android/quickstep/util/LayoutUtils.java
+++ b/quickstep/src/com/android/quickstep/util/LayoutUtils.java
@@ -43,6 +43,8 @@
         float extraSpace;
         if (dp.isVerticalBarLayout()) {
             extraSpace = 0;
+        } else if (FeatureFlags.ENABLE_HINTS_IN_OVERVIEW.get()){
+            extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx + dp.chipHintHeightPx;
         } else {
             extraSpace = dp.hotseatBarSizePx + dp.verticalDragHandleSizePx;
         }
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 1156b87..21d8144 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -20,6 +20,7 @@
 import android.os.SystemClock;
 import android.view.MotionEvent;
 
+import com.android.launcher3.Alarm;
 import com.android.launcher3.R;
 
 /**
@@ -32,11 +33,15 @@
     // The bigger this number, the easier it is to trigger the first pause.
     private static final float RAPID_DECELERATION_FACTOR = 0.6f;
 
+    /** If no motion is added for this amount of time, assume the motion has paused. */
+    private static final long FORCE_PAUSE_TIMEOUT = 300;
+
     private final float mSpeedVerySlow;
     private final float mSpeedSomewhatFast;
     private final float mSpeedFast;
     private final float mMinDisplacementForPause;
     private final float mMaxOrthogonalDisplacementForPause;
+    private final Alarm mForcePauseTimeout;
 
     private Long mPreviousTime = null;
     private Float mPreviousPosition = null;
@@ -59,6 +64,8 @@
         mMinDisplacementForPause = res.getDimension(R.dimen.motion_pause_detector_min_displacement);
         mMaxOrthogonalDisplacementForPause = res.getDimension(
                 R.dimen.motion_pause_detector_max_orthogonal_displacement);
+        mForcePauseTimeout = new Alarm();
+        mForcePauseTimeout.setOnAlarmListener(alarm -> updatePaused(true /* isPaused */));
     }
 
     /**
@@ -70,6 +77,7 @@
         if (mOnMotionPauseListener != null) {
             mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
         }
+        mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
     }
 
     /**
@@ -100,6 +108,7 @@
         }
         mPreviousTime = time;
         mPreviousPosition = position;
+        mForcePauseTimeout.setAlarm(FORCE_PAUSE_TIMEOUT);
     }
 
     private void checkMotionPaused(float velocity, float prevVelocity,
@@ -129,6 +138,10 @@
         boolean passedMaxOrthogonalDisplacement =
                 totalDisplacement.orthogonal >= mMaxOrthogonalDisplacementForPause;
         isPaused &= passedMinDisplacement && !passedMaxOrthogonalDisplacement;
+        updatePaused(isPaused);
+    }
+
+    private void updatePaused(boolean isPaused) {
         if (mIsPaused != isPaused) {
             mIsPaused = isPaused;
             if (mIsPaused) {
@@ -149,6 +162,7 @@
         mTotalDisplacement.set(0, 0);
         setOnMotionPauseListener(null);
         mIsPaused = mHasEverBeenPaused = false;
+        mForcePauseTimeout.cancelAlarm();
     }
 
     public boolean isPaused() {
diff --git a/quickstep/src/com/android/quickstep/util/SwipeAnimationTargetSet.java b/quickstep/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
deleted file mode 100644
index 4f02acf..0000000
--- a/quickstep/src/com/android/quickstep/util/SwipeAnimationTargetSet.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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.util;
-
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-
-import android.graphics.Rect;
-
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-/**
- * Extension of {@link RemoteAnimationTargetSet} with additional information about swipe
- * up animation
- */
-public class SwipeAnimationTargetSet extends RemoteAnimationTargetSet {
-
-    public final RecentsAnimationControllerCompat controller;
-    public final Rect homeContentInsets;
-    public final Rect minimizedHomeBounds;
-
-    public SwipeAnimationTargetSet(RecentsAnimationControllerCompat controller,
-            RemoteAnimationTargetCompat[] targets, Rect homeContentInsets,
-            Rect minimizedHomeBounds) {
-        super(targets, MODE_CLOSING);
-        this.controller = controller;
-        this.homeContentInsets = homeContentInsets;
-        this.minimizedHomeBounds = minimizedHomeBounds;
-    }
-
-
-    public interface SwipeAnimationListener {
-
-        void onRecentsAnimationStart(SwipeAnimationTargetSet targetSet);
-
-        void onRecentsAnimationCanceled();
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/util/TransformedRect.java b/quickstep/src/com/android/quickstep/util/TransformedRect.java
deleted file mode 100644
index 79f11e4..0000000
--- a/quickstep/src/com/android/quickstep/util/TransformedRect.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.util;
-
-import android.graphics.Rect;
-
-/**
- * A wrapper around {@link Rect} with additional transformation properties
- */
-public class TransformedRect {
-
-    public final Rect rect = new Rect();
-    public float scale = 1;
-
-    public void set(TransformedRect transformedRect) {
-        rect.set(transformedRect.rect);
-        scale = transformedRect.scale;
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
deleted file mode 100644
index a8205cd..0000000
--- a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2017 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.views;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.view.MotionEvent;
-import android.widget.FrameLayout;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Insettable;
-import com.android.launcher3.Launcher;
-import com.android.quickstep.ActivityControlHelper.LayoutListener;
-import com.android.quickstep.WindowTransformSwipeHandler;
-
-/**
- * Floating view which shows the task snapshot allowing it to be dragged and placed.
- */
-public class LauncherLayoutListener extends AbstractFloatingView
-        implements Insettable, LayoutListener {
-
-    public static LauncherLayoutListener resetAndGet(Launcher launcher) {
-        LauncherRecentsView lrv = launcher.getOverviewPanel();
-        LauncherLayoutListener listener = lrv.mLauncherLayoutListener;
-        if (listener.isOpen()) {
-            listener.close(false);
-        }
-        listener.setHandler(null);
-        return listener;
-    }
-
-    private final Launcher mLauncher;
-    private final Paint mPaint = new Paint();
-    private WindowTransformSwipeHandler mHandler;
-    private RectF mCurrentRect;
-    private float mCornerRadius;
-
-    private boolean mWillNotDraw;
-
-    /**
-     * package private
-     */
-    LauncherLayoutListener(Launcher launcher) {
-        super(launcher, null);
-        mLauncher = launcher;
-        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
-        setLayoutParams(new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
-
-        mWillNotDraw = willNotDraw();
-        super.setWillNotDraw(false);
-    }
-
-    @Override
-    public void update(boolean shouldFinish, boolean isLongSwipe, RectF currentRect,
-                  float cornerRadius) {
-        if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
-            if (shouldFinish) {
-                finish();
-            }
-            return;
-        }
-
-        mCurrentRect = currentRect;
-        mCornerRadius = cornerRadius;
-
-        setWillNotDraw(mCurrentRect == null || isLongSwipe);
-        invalidate();
-    }
-
-    @Override
-    public void setWillNotDraw(boolean willNotDraw) {
-        // Prevent super call as that causes additional relayout.
-        mWillNotDraw = willNotDraw;
-    }
-
-    @Override
-    public void setHandler(WindowTransformSwipeHandler handler) {
-        mHandler = handler;
-    }
-
-    @Override
-    public void setInsets(Rect insets) {
-        if (mHandler != null) {
-            mHandler.buildAnimationController();
-        }
-    }
-
-    @Override
-    public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
-        return false;
-    }
-
-    @Override
-    protected void handleClose(boolean animate) {
-        if (mIsOpen) {
-            mIsOpen = false;
-            // We don't support animate.
-            mLauncher.getDragLayer().removeView(this);
-
-            if (mHandler != null) {
-                mHandler.layoutListenerClosed();
-            }
-        }
-    }
-
-    @Override
-    public void open() {
-        if (!mIsOpen) {
-            mLauncher.getDragLayer().addView(this);
-            mIsOpen = true;
-        }
-    }
-
-    @Override
-    public void logActionCommand(int command) {
-        // We should probably log the weather
-    }
-
-    @Override
-    protected boolean isOfType(int type) {
-        return (type & TYPE_QUICKSTEP_PREVIEW) != 0;
-    }
-
-    @Override
-    public void finish() {
-        close(false);
-        setHandler(null);
-        mLauncher.getRotationHelper().setStateHandlerRequest(REQUEST_NONE);
-    }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (!mWillNotDraw) {
-            canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
-        }
-    }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
index b801b4f..7274090 100644
--- a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
+++ b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
@@ -90,11 +90,16 @@
                     base.evaluate();
                 }
 
-                private void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride) {
+                private void overrideSwipeUpEnabled(Boolean swipeUpEnabledOverride)
+                        throws Throwable {
                     mLauncher.overrideSwipeUpEnabled(swipeUpEnabledOverride);
                     mMainThreadExecutor.execute(() -> OverviewInteractionState.INSTANCE.get(
                             InstrumentationRegistry.getInstrumentation().getTargetContext()).
                             notifySwipeUpSettingChanged(mLauncher.isSwipeUpEnabled()));
+                    // TODO(b/124236673): avoid using sleep().
+                    mLauncher.getDevice().waitForIdle();
+                    Thread.sleep(2000);
+                    mLauncher.getDevice().waitForIdle();
                 }
             };
         } else {
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index c243968..fe789aa 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -63,11 +63,11 @@
     public void testPressHome() {
         runTest(enterEvt(Launcher.ON_CREATE_EVT),
                 exitEvt(Launcher.ON_CREATE_EVT),
-                enterEvt(OtherActivityTouchConsumer.DOWN_EVT),
-                exitEvt(OtherActivityTouchConsumer.DOWN_EVT));
+                enterEvt(OtherActivityInputConsumer.DOWN_EVT),
+                exitEvt(OtherActivityInputConsumer.DOWN_EVT));
 
-        runTest(enterEvt(OtherActivityTouchConsumer.DOWN_EVT),
-                exitEvt(OtherActivityTouchConsumer.DOWN_EVT),
+        runTest(enterEvt(OtherActivityInputConsumer.DOWN_EVT),
+                exitEvt(OtherActivityInputConsumer.DOWN_EVT),
                 enterEvt(Launcher.ON_CREATE_EVT),
                 exitEvt(Launcher.ON_CREATE_EVT));
     }
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index e9d8bce..c60cf45 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -55,12 +55,7 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
-
-        clearLauncherData();
-
-        mLauncher.pressHome();
-        waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
-        waitForResumed("Launcher internal state is still Background");
+        TaplTestsLauncher3.initialize(this);
     }
 
     private void startTestApps() throws Exception {
diff --git a/res/layout/hint_container.xml b/res/layout/hint_container.xml
new file mode 100644
index 0000000..75aa913
--- /dev/null
+++ b/res/layout/hint_container.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<merge/>
\ No newline at end of file
diff --git a/res/layout/launcher.xml b/res/layout/launcher.xml
index 87078b9..91c3705 100644
--- a/res/layout/launcher.xml
+++ b/res/layout/launcher.xml
@@ -43,6 +43,11 @@
             layout="@layout/overview_panel"
             android:visibility="gone" />
 
+        <include
+            android:id="@+id/hints"
+            layout="@layout/hint_container"
+            android:visibility="gone"/>
+
         <!-- Keep these behind the workspace so that they are not visible when
          we go into AllApps -->
         <com.android.launcher3.pageindicators.WorkspacePageIndicator
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 8fdc27b..ca59afa 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Vouer: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Style en muurpapiere"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Tuis-instellings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gedeaktiveer deur jou administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Laat toe dat tuisskerm gedraai word"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 4c80204..d942e9e 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"አቃፊ፦ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ቅጦች እና ልጣፎች"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"የመነሻ ቅንብሮች"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"በእርስዎ አስተዳዳሪ የተሰናከለ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"የመነሻ ማያ ገጽ ማሽከርከርን ይፍቀዱ"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 72f6e6a..bf00e95 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -84,8 +84,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"المجلد: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"الأنماط والخلفيات"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"إعدادات الصفحة الرئيسية"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"أوقف المشرف هذه الميزة"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"السماح بتدوير الشاشة الرئيسية"</string>
diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml
index f528f2b..2984603 100644
--- a/res/values-as/strings.xml
+++ b/res/values-as/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ফ’ল্ডাৰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ৱিজেটসমূহ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ৱালপেপাৰসমূহ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ষ্টাইল &amp; ৱালপেপাৰ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"গৃহ ছেটিংসমূহ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপোনাৰ প্ৰশাসকে অক্ষম কৰি ৰাখিছে"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"গৃহ স্ক্ৰীণ ঘূৰোৱাৰ অনুমতি দিয়ক"</string>
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 26b5da5..5528a19 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Qovluq: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidcet"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Divar kağızları"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Üslub və divar kağızları"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home ayarları"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Admininiz tərəfindən deaktiv edilib"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Əsas ekranın firlanmağına icazə verin"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 7306b02..5158a78 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -81,8 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Direktorijum: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidžeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadine"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Podešavanja početnog ekrana"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator je onemogućio"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotaciju početnog ekrana"</string>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index a30b403..b4cf913 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Віджэты"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Шпалеры"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стылі і шпалеры"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Налады галоўнага экрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Адключаная адміністратарам"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дазволіць паварот галоўнага экрана"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 360a036..408f205 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилове и тапети"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Настройки за началния екран"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Деактивирано от администратора ви"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешаване на завъртането на началния екран"</string>
diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml
index 59f7d29..6d80329 100644
--- a/res/values-bn/strings.xml
+++ b/res/values-bn/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"উইজেট"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"স্টাইল এবং ওয়ালপেপার"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"হোম সেটিংস"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"আপনার প্রশাসক দ্বারা অক্ষম করা হয়েছে"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"হোমস্ক্রীন ঘোরানোর অনুমতি দিন"</string>
diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml
index 803fee8..53bdeb2 100644
--- a/res/values-bs/strings.xml
+++ b/res/values-bs/strings.xml
@@ -81,8 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Dodaci"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadinske slike"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog ekrana"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio vaš administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Dozvoli rotiranje početnog ekrana"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 02180dc..56ef414 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estils i fons de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configuració de pantalla d\'inici"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desactivada per l\'administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permet la rotació de la pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 0d78e54..692b57d 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Složka: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styly a tapety"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavení plochy"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázáno administrátorem"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Povolit otáčení plochy"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index e1f1b63..a471e50 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilarter og baggrunde"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Indstillinger for startskærmen"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Deaktiveret af din administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillad rotation af startskærmen"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 4fcb8a0..1b7cd22 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Ordner: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stile und Hintergründe"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Einstellungen für den Startbildschirm"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Von deinem Administrator deaktiviert"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Drehung des Startbildschirms zulassen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 73d88a1..2363e61 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Φάκελος: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Στιλ και ταπετσαρίες"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ρυθμίσεις Αρχ. Οθ."</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Απενεργοποιήθηκε από τον διαχειριστή σας"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Να επιτρέπεται η περιστροφή της αρχικής οθόνης"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 0061afb..6692370 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 0061afb..6692370 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 0061afb..6692370 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles &amp; wallpapers"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home settings"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disabled by your admin"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Allow Homescreen rotation"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 3a72f4c..d10c84b 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos y fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configuración de página principal"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"El administrador inhabilitó esta función"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir la rotación de la pantalla principal"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index bad7520..ac6ffaa 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Carpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos y fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ajustes de la pantalla de inicio"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inhabilitado por el administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotación de la pantalla de inicio"</string>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index 5892d09..cea321d 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Kaust: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiilid ja taustapildid"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Avaekraani seaded"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Keelas administraator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Luba avaekraani pööramine"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index e82474e..18af2fb 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estiloak eta horma-paperak"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Hasierako pantailaren ezarpenak"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratzaileak desgaitu du"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Baimendu hasierako pantaila biratzea"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 7ea9262..7e3cbdc 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"پوشه: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ابزارک‌ها"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواری‌ها"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"سبک و کاغذدیواری"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"تنظیمات صفحه اصلی"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"توسط سرپرست سیستم غیرفعال شده است"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"امکان دادن به چرخش صفحه اصلی"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9b56c0f..f87441f 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Kansio: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Tyylit ja taustakuvat"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Kotiasetukset"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Järjestelmänvalvoja on poistanut toiminnon käytöstä."</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Salli aloitusnäytön kiertäminen"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 3b947aa..9d0e3a0 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier : <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Cette fonction est désactivée par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 6a95e19..585139c 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dossier \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Paramètres accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml
index e6e6ed1..769586e 100644
--- a/res/values-gl/strings.xml
+++ b/res/values-gl/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e fondos de pantalla"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configuración da pantalla de Inicio"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Función desactivada polo administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir xirar a pantalla de inicio"</string>
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 8dfcb2c..7def3ac 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ફોલ્ડર: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"વિજેટ્સ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"વૉલપેપર્સ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"શૈલીઓ અને વૉલપેપર"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"હોમ સેટિંગ્સ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"તમારા વ્યવસ્થાપક દ્વારા અક્ષમ કરેલ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"હોમ સ્ક્રીનને ફેરવવાની મંજૂરી આપો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 0b39ffe..5f24b05 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"फ़ोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"शॉर्टकट"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"स्टाइल और वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"होम पेज की सेटिंग"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपके एडमिन ने बंद किया हुआ है"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"होमस्क्रीन घुमाने की अनुमति दें"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 8ea98d6..1caa27f 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -81,8 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadine"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilovi i pozadine"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Postavke početnog zaslona"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogućio administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Dopusti zakretanje početnog zaslona"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index ae0dba3..3327590 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stílusok és háttérképek"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Kezdőoldal beállításai"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"A rendszergazda letiltotta"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"A kezdőképernyő elforgatásának engedélyezése"</string>
diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml
index 7542b75..fbb657e 100644
--- a/res/values-hy/strings.xml
+++ b/res/values-hy/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Պանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Ոճեր և պաստառներ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Գլխավոր էջի կարգավորումներ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Անջատվել է ձեր ադմինիստրատորի կողմից"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Թույլ տալ հիմնական էկրանի պտտումը"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 96b3d92..0841be0 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya &amp; wallpaper"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Setelan layar Utama"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dinonaktifkan oleh admin"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Izinkan layar Utama diputar"</string>
diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml
index 5673a2d..d66ca32 100644
--- a/res/values-is/strings.xml
+++ b/res/values-is/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stílar og veggfóður"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Heimastillingar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Gert óvirkt af kerfisstjóra"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Leyfa snúning fyrir heimaskjá"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 9cd5c0f..8c4e3c5 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Cartella: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stili e sfondi"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Impostazioni Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Disattivata dall\'amministratore"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Consenti rotazione della schermata Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 64022ce..6f5a400 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"תיקיה: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"רכיבי ווידג\'ט"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"סגנונות וטפטים"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"הגדרות דף הבית"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"הושבת על ידי מנהל המערכת שלך"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"אפשרות סיבוב של מסך דף הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 952c86b..354f020 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"フォルダ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"スタイルと壁紙"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ホームの設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"管理者により無効にされています"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ホーム画面の回転を許可"</string>
diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml
index efcaeab..e9afef9 100644
--- a/res/values-ka/strings.xml
+++ b/res/values-ka/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"საქაღალდე: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"სტილები და ფონები"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"მთავარი გვერდის პარამეტრები"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"გათიშულია თქვენი ადმინისტრატორის მიერ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"მთავარი ეკრანის შეტრიალების დაშვება"</string>
diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml
index fb1f71d..2e953a6 100644
--- a/res/values-kk/strings.xml
+++ b/res/values-kk/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тұсқағаздар"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стильдер мен тұсқағаздар"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Негізгі экран параметрлері"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Әкімші өшірді"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Негізгі экранның бұрылуына рұқсат ету"</string>
diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml
index 59a3666..b953ea3 100644
--- a/res/values-km/strings.xml
+++ b/res/values-km/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ថត៖ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ធាតុ​ក្រាហ្វិក"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំង​រូបភាព"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"រចនាប័ទ្ម និង​ផ្ទាំង​រូបភាព"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ការកំណត់​ទំព័រដើម"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"បានបិទដំណើរការដោយអ្នកគ្រប់គ្រងរបស់អ្នក"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"អនុញ្ញាតការបងិ្វលអេក្រង់ដើម"</string>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 9fc4aec..2e96a7c 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್‌ಗಳು"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ಶೈಲಿಗಳು &amp; ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ಮುಖಪುಟ ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದ್ದಾರೆ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ಮುಖಪುಟ ತಿರುಗುವಿಕೆಯನ್ನು ಅನುಮತಿಸಿ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index 506ffc5..c58620e 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"폴더: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"스타일 및 배경화면"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"홈 설정"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"관리자가 사용 중지함"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"홈 화면 회전 허용"</string>
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 433bb98..36b6385 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилдер жана тушкагаздар"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Башкы беттин жөндөөлөрү"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администраторуңуз өчүрүп койгон"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Башкы экранды айлантууга уруксат берүү"</string>
diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml
index 2664021..afe7664 100644
--- a/res/values-lo/strings.xml
+++ b/res/values-lo/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ໂຟນເດີ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ຮູບແບບ ແລະ ຮູບພື້ນຫຼັງ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ການຕັ້ງຄ່າ Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ຖືກປິດການນຳໃຊ້ໂດຍຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ອະນຸຍາດໃຫ້ໝຸນໜ້າຈໍທຳອິດໄດ້"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 6dbf1d6..6571582 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Aplankas: „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiliai ir ekrano fonai"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"„Home“ nustatymai"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Išjungė administratorius"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Leisti pasukti pagrindinį ekraną"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 2a9b772..3da0fbb 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -81,8 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mape: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stili un fona tapetes"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Sākumlapas iestatījumi"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Atspējojis administrators"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Atļaut sākuma ekrāna pagriešanu"</string>
diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml
index 3d4151c..9550a5f 100644
--- a/res/values-mk/strings.xml
+++ b/res/values-mk/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и тапети"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Поставки за Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Оневозможено од администраторот"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволете ротација на Почетниот екран"</string>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index c3cb059..d4bfbc1 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പർ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"സ്‌റ്റൈലുകളും വാൾപേപ്പറുകളും"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ഹോം ക്രമീകരണം"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"അഡ്മിൻ പ്രവർത്തനരഹിതമാക്കിയിരിക്കുന്നു"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ഹോം സ്ക്രീൻ തിരിക്കൽ അനുവദിക്കുക"</string>
diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml
index ac01fa2..f52f461 100644
--- a/res/values-mn/strings.xml
+++ b/res/values-mn/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Загвар ба ханын зураг"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Нүүр хуудасны тохиргоо"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Таны админ идэвхгүй болгосон"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Нүүр дэлгэцийг эргүүлэхийг зөвшөөрөх"</string>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index 3be13ef..60dc9a6 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली आणि वॉलपेपर"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"होम सेटिंग्‍ज"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"आपल्या प्रशासकाने अक्षम केले"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"मुख्यस्क्रीन फिरविण्‍यास अनुमती द्या"</string>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index a97e201..7d05412 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Gaya &amp; kertas dinding"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Tetapan laman utama"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dilumpuhkan oleh pentadbir anda"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Benarkan putaran Skrin Utama"</string>
diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml
index 2e1e0d4..7ae4446 100644
--- a/res/values-my/strings.xml
+++ b/res/values-my/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ဝိဂျက်များ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ပုံစံနှင့် နောက်ခံပုံများ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ပင်မဆက်တင်များ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"သင့်စီမံခန့်ခွဲသူက ပိတ်လိုက်ပါသည်"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ပင်မစာမျက်နှာလှည့်ခြင်းကို ခွင့်ပြုပါ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index e3e1768..2257367 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mappe: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiler og bakgrunner"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Startsideinnstillinger"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administratoren har slått av funksjonen"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillat rotasjon av startskjermen"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index ea6fdf5..1e7aee9 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"शैली तथा वालपेपरहरू"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"गृहपृष्ठका सेटिङहरू"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"तपाईँको प्रशासकद्वारा असक्षम गरिएको"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"गृह स्क्रिनलाई घुम्ने अनुमति दिनुहोस्"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 1ae31be..bc1b450 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Map: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergrond"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stijlen en achtergronden"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Instellingen startscherm"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Uitgeschakeld door je beheerder"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Draaien van startscherm toestaan"</string>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index 7959e11..4ddc903 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ଫୋଲ୍ଡର: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ୱିଜେଟ୍‌"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ୱାଲପେପର୍‌"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ଶୈଳୀ ଏବଂ ୱାଲ୍‍‍ପେପର୍"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ହୋମ୍‌ ସେଟିଙ୍ଗ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ଆପଣଙ୍କ ଆଡମିନଙ୍କ ଦ୍ୱାରା ଅକ୍ଷମ କରାଯାଇଛି"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ହୋମ୍‌ ସ୍କ୍ରୀନ୍ ବୁଲାଇବା ଅନୁମତି ଦିଅନ୍ତୁ"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index e740cb9..0bd1911 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ਫੋਲਡਰ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ਵਿਜੇਟ"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"ਵਾਲਪੇਪਰ"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ਸ਼ੈਲੀਆਂ ਅਤੇ ਵਾਲਪੇਪਰ"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ਹੋਮ ਸੈਟਿੰਗਾਂ"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ਤੁਹਾਡੇ ਪ੍ਰਸ਼ਾਸਕ ਦੁਆਰਾ ਅਯੋਗ ਬਣਾਈ ਗਈ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ਹੋਮ ਸਕ੍ਰੀਨ ਨੂੰ ਘੁੰਮਾਉਣ ਦੀ ਆਗਿਆ ਦਿਓ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index bab3895..e33ef7d 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Style i tapety"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ustawienia strony głównej"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Funkcja wyłączona przez administratora"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Zezwalaj na obrót ekranu głównego"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 7cd96a5..b26b812 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e imagens de fundo"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Definições da página inicial"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativada pelo gestor"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação do ecrã principal"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 7c90a14..1744fa1 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Pasta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Estilos e planos de fundo"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Configurações da tela inicial"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Desativado pelo administrador"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permitir rotação da tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 6885de5..a7e8f05 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -81,8 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dosar: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiluri și imagini de fundal"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Setări pentru ecranul de pornire"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Dezactivată de administrator"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Permiteți rotirea ecranului de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 56c9a4f..9030a75 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Темы и обои"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Настройки главного экрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Функция отключена администратором"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Разрешить поворачивать главный экран"</string>
diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml
index d3737be..574d3ed 100644
--- a/res/values-si/strings.xml
+++ b/res/values-si/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"විලාස සහ බිතුපත්"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home සැකසීම්"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ඔබගේ පරිපාලක විසින් අබල කරන ලදී"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"මුල් පිටු තිරය කරකැවීමට ඉඩ දෙන්න"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 6d64792..77eeab2 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Priečinok: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Štýly a tapety"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavenia Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Zakázané vaším správcom"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Povoliť otáčanie plochy"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 3f1b14c..044a4b4 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mapa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Slogi in ozadja"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Nastavitve začetnega zaslona"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Onemogočil skrbnik."</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Omogočanje sukanja začetnega zaslona"</string>
diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml
index 31d3f4c..48ae26e 100644
--- a/res/values-sq/strings.xml
+++ b/res/values-sq/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Dosja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Miniaplikacionet"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Imazhet e sfondit"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stilet dhe imazhet e sfondit"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Cilësimet e Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Çaktivizuar nga administratori"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Lejo rrotullimin e ekranit kryesor"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 5a2c0af..f74fde2 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -81,8 +81,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Директоријум: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилови и позадине"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Подешавања почетног екрана"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Администратор је онемогућио"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволи ротацију почетног екрана"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 74d434e..e7400a6 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Mapp: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Format och bakgrunder"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Startinställningar"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Inaktiverat av administratören"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Tillåt rotering av startskärmen"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 1483dcb..7b84ed3 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mitindo na mandhari"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Mipangilio ya mwanzo"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Imezimwa na msimamizi wako"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Ruhusu kuzungusha skrini ya Kwanza"</string>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 17c0adb..47b2581 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -26,7 +26,7 @@
     <string name="activity_not_available" msgid="7456344436509528827">"பயன்பாடு இல்லை"</string>
     <string name="safemode_shortcut_error" msgid="9160126848219158407">"இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது"</string>
     <string name="safemode_widget_error" msgid="4863470563535682004">"பாதுகாப்புப் பயன்முறையில் விட்ஜெட்கள் முடக்கப்பட்டுள்ளன"</string>
-    <string name="shortcut_not_available" msgid="2536503539825726397">"குறுக்குவழி இல்லை"</string>
+    <string name="shortcut_not_available" msgid="2536503539825726397">"ஷார்ட்கட் இல்லை"</string>
     <string name="home_screen" msgid="806512411299847073">"முகப்புத் திரை"</string>
     <string name="custom_actions" msgid="3747508247759093328">"தனிப்பயன் செயல்கள்"</string>
     <string name="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் பிடிக்கவும்."</string>
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ஷார்ட்கட்ஸ்"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"ஸ்டைல்கள் &amp; வால்பேப்பர்கள்"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"முகப்பு அமைப்புகள்"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"உங்கள் நிர்வாகி முடக்கியுள்ளார்"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"முகப்புத் திரை சுழற்சியை அனுமதி"</string>
@@ -127,13 +126,13 @@
     <string name="action_decrease_width" msgid="1374549771083094654">"அகலத்தைக் குறை"</string>
     <string name="action_decrease_height" msgid="282377193880900022">"உயரத்தைக் குறை"</string>
     <string name="widget_resized" msgid="9130327887929620">"அகலம் <xliff:g id="NUMBER_0">%1$s</xliff:g> மற்றும் உயரம் <xliff:g id="NUMBER_1">%2$s</xliff:g>க்கு விட்ஜெட் அளவு மாற்றப்பட்டது"</string>
-    <string name="action_deep_shortcut" msgid="2864038805849372848">"குறுக்குவழிகள்"</string>
+    <string name="action_deep_shortcut" msgid="2864038805849372848">"ஷார்ட்கட்கள்"</string>
     <string name="shortcuts_menu_with_notifications_description" msgid="2676582286544232849">"ஷார்ட்கட்கள் மற்றும் அறிவிப்புகள்"</string>
     <string name="action_dismiss_notification" msgid="5909461085055959187">"நிராகரி"</string>
     <string name="notification_dismissed" msgid="6002233469409822874">"அறிவிப்பு நிராகரிக்கப்பட்டது"</string>
     <string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
     <string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
-    <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணி விவரம்"</string>
+    <string name="work_profile_toggle_label" msgid="3081029915775481146">"பணிக் கணக்கு"</string>
     <string name="bottom_work_tab_user_education_title" msgid="5785851780786322825">"பணி ஆப்ஸை இங்கு காணலாம்"</string>
     <string name="bottom_work_tab_user_education_body" msgid="2818107472360579152">"ஒவ்வொரு பணிப் பயன்பாடும் ஒரு பேட்ஜைக் கொண்டிருக்கும். இவை, ஆப்ஸ் உங்கள் நிறுவனத்தால் பாதுகாப்பாக வைக்கப்பட்டுள்ளன என்பதைக் குறிக்கின்றன. இந்த ஆப்ஸை எளிதாக அணுக, முகப்புத் திரைக்கு நகர்த்திக்கொள்ளவும்."</string>
     <string name="work_mode_on_label" msgid="4781128097185272916">"உங்கள் நிறுவனம் நிர்வகிக்கிறது"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 48dd872..1b74be1 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్‌లు"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్‌పేపర్‌లు"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"శైలులు &amp; వాల్‌పేపర్‌లు"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"హోమ్ సెట్టింగ్‌లు"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"మీ నిర్వాహకులు నిలిపివేసారు"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"హోమ్ స్క్రీన్ భ్రమణాన్ని అనుమతించండి"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 829a266..fee76e6 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"โฟลเดอร์: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"รูปแบบและวอลเปเปอร์"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"การตั้งค่าหน้าแรก"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"ปิดใช้โดยผู้ดูแลระบบ"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"อนุญาตให้หมุนหน้าจอหลัก"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index d2b6915..359934e 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Folder: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mga istilo at wallpaper"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Mga setting ng Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Na-disable ng iyong admin"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Payagan ang pag-rotate ng Home screen"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index edd5ed8..56f8447 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Klasör: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Stiller ve duvar kağıtları"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Ana ekran ayarları"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Yöneticiniz tarafından devre dışı bırakıldı"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Ana ekranı döndürmeye izin ver"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index 9523443..ad51464 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -82,8 +82,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Папка <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Стилі й фонові малюнки"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Налаштування Home"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Вимкнув адміністратор"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Дозволити обертання головного екрана"</string>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index c01de26..5d7218d 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"طرزیں اور وال پیپر"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"ہوم ترتیبات"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"آپ کے منتظم کی طرف سے غیر فعال کر دیا گیا"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"ہوم اسکرین گھمانے کی اجازت دیں"</string>
diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml
index d184b5c..d34a64f 100644
--- a/res/values-uz/strings.xml
+++ b/res/values-uz/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fon rasmlari"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Mavzu va fon rasmlari"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Bosh ekran sozlamalari"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Administrator tomonidan o‘chirilgan"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Asosiy ekranni aylantirishga ruxsat berish"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 5570567..174ec7f 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Thư mục: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Kiểu và hình nền"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Cài đặt màn hình chính"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Bị tắt bởi quản trị viên của bạn"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Cho phép xoay Màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3a99dec..fd31875 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"文件夹:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"微件"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"样式和壁纸"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"主屏幕设置"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已被您的管理员停用"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允许旋转主屏幕"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 39bf109..de0fa89 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"樣式和桌布"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"主螢幕設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由您的管理員停用"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允許主畫面旋轉"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 358e6e2..4d37d72 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"資料夾:<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"樣式和桌布"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Home 設定"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"已由你的管理員停用"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"允許旋轉主螢幕"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 614ae35..83178fe 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -80,8 +80,7 @@
     <string name="folder_name_format" msgid="6629239338071103179">"Ifolda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
-    <!-- no translation found for styles_wallpaper_button_text (4342122323125579619) -->
-    <skip />
+    <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Izitayela nezithombe zangemuva"</string>
     <string name="settings_button_text" msgid="8873672322605444408">"Izilungiselelo zasekhaya"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Kukhutshazwe umlawuli wakho"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Vumela ukuphendukiswa kwesikrini sasekhaya"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 078ce60..04e4591 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -236,4 +236,8 @@
     <dimen name="snackbar_elevation">3dp</dimen>
     <dimen name="snackbar_min_text_size">12sp</dimen>
     <dimen name="snackbar_max_text_size">14sp</dimen>
+
+<!-- Hints -->
+    <dimen name="chip_hint_height">26dp</dimen>
+    <dimen name="chip_hint_bottom_margin">194dp</dimen>
 </resources>
diff --git a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
index 54038d2..656d55c 100644
--- a/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
+++ b/robolectric_tests/src/com/android/launcher3/config/FlagOverrideSampleTest.java
@@ -26,7 +26,6 @@
     @Test
     public void withFlagOn() {
         assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
-        assertFalse(FeatureFlags.QUICK_SWITCH.get());
         assertFalse(FeatureFlags.STYLE_WALLPAPER.get());
     }
 
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index e75527e..599a353 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -55,7 +55,6 @@
             TYPE_DISCOVERY_BOUNCE,
             TYPE_SNACKBAR,
 
-            TYPE_QUICKSTEP_PREVIEW,
             TYPE_TASK_MENU,
             TYPE_OPTIONS_POPUP
     })
@@ -71,25 +70,23 @@
     public static final int TYPE_SNACKBAR = 1 << 7;
 
     // Popups related to quickstep UI
-    public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 8;
-    public static final int TYPE_TASK_MENU = 1 << 9;
-    public static final int TYPE_OPTIONS_POPUP = 1 << 10;
+    public static final int TYPE_TASK_MENU = 1 << 8;
+    public static final int TYPE_OPTIONS_POPUP = 1 << 9;
 
     public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
             | TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
-            | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
+            | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
             | TYPE_OPTIONS_POPUP | TYPE_SNACKBAR;
 
     // Type of popups which should be kept open during launcher rebind
     public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
-            | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
+            | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE;
 
     // Usually we show the back button when a floating view is open. Instead, hide for these types.
     public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
             | TYPE_SNACKBAR;
 
-    public static final int TYPE_ACCESSIBLE = TYPE_ALL
-            & ~TYPE_DISCOVERY_BOUNCE & ~TYPE_QUICKSTEP_PREVIEW;
+    public static final int TYPE_ACCESSIBLE = TYPE_ALL & ~TYPE_DISCOVERY_BOUNCE;
 
     // These view all have particular operation associated with swipe down interaction.
     public static final int TYPE_STATUS_BAR_SWIPE_DOWN_DISALLOW = TYPE_WIDGETS_BOTTOM_SHEET |
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 70c8aaa..4c6824a 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -91,6 +91,7 @@
             mText = getResources().getString(canRemove(item)
                     ? R.string.remove_drop_target_label
                     : android.R.string.cancel);
+            setContentDescription(mText);
             requestLayout();
         }
     }
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 812cf9f..01535b0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -26,12 +26,15 @@
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.view.Surface;
+import android.view.View;
 import android.view.WindowManager;
 
 import com.android.launcher3.CellLayout.ContainerType;
 import com.android.launcher3.icons.DotRenderer;
 import com.android.launcher3.icons.IconNormalizer;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+
 public class DeviceProfile {
 
     public final InvariantDeviceProfile inv;
@@ -100,6 +103,10 @@
     public int folderChildTextSizePx;
     public int folderChildDrawablePaddingPx;
 
+    // Hints
+    public int chipHintHeightPx;
+    public int chipHintBottomMarginPx;
+
     // Hotseat
     public int hotseatCellHeightPx;
     // In portrait: size = height, in landscape: size = width
@@ -197,6 +204,9 @@
 
         workspaceCellPaddingXPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_padding_x);
 
+        chipHintHeightPx = res.getDimensionPixelSize(R.dimen.chip_hint_height);
+        chipHintBottomMarginPx = res.getDimensionPixelSize(R.dimen.chip_hint_bottom_margin);
+
         hotseatBarTopPaddingPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_top_padding);
         hotseatBarBottomPaddingPx = (isTallDevice ? 0
@@ -312,7 +322,7 @@
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
         float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
-        iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
+        iconSizePx = Math.max(1, (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale));
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
 
@@ -574,6 +584,33 @@
         }
     }
 
+    /**
+     * Gets an item's location on the home screen. This is useful if the home screen
+     * is animating, otherwise use {@link View#getLocationOnScreen(int[])}.
+     *
+     * TODO(b/123900446): Handle landscape mode
+     * @param pageDiff The page difference relative to the current page.
+     */
+    public void getItemLocation(int cellX, int cellY, int spanX, int spanY, int container,
+            int pageDiff, Rect outBounds) {
+        outBounds.setEmpty();
+        outBounds.left = mInsets.left
+                + workspacePadding.left + cellLayoutPaddingLeftRightPx + (cellX * getCellSize().x);
+        outBounds.top = mInsets.top;
+        if (container == CONTAINER_HOTSEAT) {
+            outBounds.top += workspacePadding.top
+                    + (inv.numRows * getCellSize().y)
+                    + verticalDragHandleSizePx
+                    - verticalDragHandleOverlapWorkspace;
+            outBounds.bottom = outBounds.top + hotseatBarSizePx - hotseatBarBottomPaddingPx;
+        } else {
+            outBounds.top += workspacePadding.top + (cellY * getCellSize().y);
+            outBounds.bottom = outBounds.top + (getCellSize().y * spanY);
+            outBounds.left += (pageDiff) * availableWidthPx;
+        }
+        outBounds.right = outBounds.left + (getCellSize().x * spanX);
+    }
+
     private static Context getContext(Context c, int orientation) {
         Configuration context = new Configuration(c.getResources().getConfiguration());
         context.orientation = orientation;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 245e470..f571aa3 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -257,9 +257,8 @@
 
     private RotationHelper mRotationHelper;
 
-
     private final Handler mHandler = new Handler();
-    private final Runnable mLogOnDelayedResume = this::logOnDelayedResume;
+    private final Runnable mHandleDeferredResume = this::handleDeferredResume;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -756,6 +755,7 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onStop();
         }
+
         getUserEventDispatcher().logActionCommand(Action.Command.STOP,
                 mStateManager.getState().containerType, -1);
 
@@ -782,11 +782,13 @@
         RaceConditionTracker.onEvent(ON_START_EVT, EXIT);
     }
 
-    private void logOnDelayedResume() {
+    private void handleDeferredResume() {
         if (hasBeenResumed()) {
             getUserEventDispatcher().logActionCommand(Action.Command.RESUME,
                     mStateManager.getState().containerType, -1);
             getUserEventDispatcher().startSession();
+
+            UiFactory.onLauncherStateOrResumeChanged(this);
         }
     }
 
@@ -797,8 +799,8 @@
         super.onResume();
         TraceHelper.partitionSection("ON_RESUME", "superCall");
 
-        mHandler.removeCallbacks(mLogOnDelayedResume);
-        Utilities.postAsyncCallback(mHandler, mLogOnDelayedResume);
+        mHandler.removeCallbacks(mHandleDeferredResume);
+        Utilities.postAsyncCallback(mHandler, mHandleDeferredResume);
 
         setOnResumeCallback(null);
         // Process any items that were added while Launcher was away.
@@ -812,7 +814,6 @@
         if (mLauncherCallbacks != null) {
             mLauncherCallbacks.onResume();
         }
-        UiFactory.onLauncherStateOrResumeChanged(this);
 
         TraceHelper.endSection("ON_RESUME");
         RaceConditionTracker.onEvent(ON_RESUME_EVT, EXIT);
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index beef97e..6d85612 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -19,6 +19,11 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
 import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
 
+import static com.android.launcher3.TestProtocol.ALL_APPS_STATE_ORDINAL;
+import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
+import static com.android.launcher3.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
 import static com.android.launcher3.anim.Interpolators.ACCEL_2;
 import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
 
@@ -27,7 +32,6 @@
 import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.uioverrides.AllAppsState;
 import com.android.launcher3.uioverrides.BackgroundAppState;
-import com.android.launcher3.uioverrides.FastOverviewState;
 import com.android.launcher3.uioverrides.OverviewState;
 import com.android.launcher3.uioverrides.UiFactory;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
@@ -78,18 +82,20 @@
     /**
      * TODO: Create a separate class for NORMAL state.
      */
-    public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE, 0,
+    public static final LauncherState NORMAL = new LauncherState(NORMAL_STATE_ORDINAL,
+            ContainerType.WORKSPACE, 0,
             FLAG_DISABLE_RESTORE | FLAG_WORKSPACE_ICONS_CAN_BE_DRAGGED | FLAG_HIDE_BACK_BUTTON |
             FLAG_HAS_SYS_UI_SCRIM);
 
     /**
      * Various Launcher states arranged in the increasing order of UI layers
      */
-    public static final LauncherState SPRING_LOADED = new SpringLoadedState(1);
-    public static final LauncherState OVERVIEW = new OverviewState(2);
-    public static final LauncherState FAST_OVERVIEW = new FastOverviewState(3);
-    public static final LauncherState ALL_APPS = new AllAppsState(4);
-    public static final LauncherState BACKGROUND_APP = new BackgroundAppState(5);
+    public static final LauncherState SPRING_LOADED = new SpringLoadedState(
+            SPRING_LOADED_STATE_ORDINAL);
+    public static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
+    public static final LauncherState ALL_APPS = new AllAppsState(ALL_APPS_STATE_ORDINAL);
+    public static final LauncherState BACKGROUND_APP = new BackgroundAppState(
+            BACKGROUND_APP_STATE_ORDINAL);
 
     public final int ordinal;
 
@@ -187,6 +193,7 @@
      *   translationY factor where 0 is top aligned and 0.5 is centered vertically
      */
     public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+        // TODO: Simplify to use a constant value instead of a factor.
         return new float[] {1.1f, 0f};
     }
 
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index df8ac99..a877141 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -147,10 +147,17 @@
     }
 
     /**
+     * Returns true if the state changes should be animated.
+     */
+    public boolean shouldAnimateStateChange() {
+        return !mLauncher.isForceInvisible() && mLauncher.isStarted();
+    }
+
+    /**
      * @see #goToState(LauncherState, boolean, Runnable)
      */
     public void goToState(LauncherState state) {
-        goToState(state, !mLauncher.isForceInvisible() && mLauncher.isStarted() /* animated */);
+        goToState(state, shouldAnimateStateChange());
     }
 
     /**
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 018ec5f..00da6fa 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1024,8 +1024,8 @@
             return;
         }
 
-        if (amount < 0) {
-            super.scrollTo(amount, getScrollY());
+        if (overScrollAmount < 0) {
+            super.scrollTo(overScrollAmount, getScrollY());
         } else {
             super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
         }
@@ -1134,7 +1134,9 @@
                 final int activePointerId = mActivePointerId;
                 final int pointerIndex = ev.findPointerIndex(activePointerId);
                 final float x = ev.getX(pointerIndex);
-                int velocityX = computeXVelocity();
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
                 final int deltaX = (int) (x - mDownMotionX);
                 final int pageWidth = getPageAt(mCurrentPage).getMeasuredWidth();
                 boolean isSignificantMove = Math.abs(deltaX) > pageWidth *
@@ -1238,12 +1240,6 @@
         return true;
     }
 
-    protected int computeXVelocity() {
-        final VelocityTracker velocityTracker = mVelocityTracker;
-        velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-        return (int) velocityTracker.getXVelocity(mActivePointerId);
-    }
-
     protected boolean shouldFlingForVelocity(int velocityX) {
         return Math.abs(velocityX) > mFlingThresholdVelocity;
     }
diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index 0a3b86d..23df79e 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -22,6 +22,12 @@
 public final class TestProtocol {
     public static final String GET_SCROLL_MESSAGE = "TAPL_GET_SCROLL";
     public static final String SCROLL_Y_FIELD = "scrollY";
+    public static final String STATE_FIELD = "state";
     public static final String SWITCHED_TO_STATE_MESSAGE = "TAPL_SWITCHED_TO_STATE";
     public static final String RESPONSE_MESSAGE_POSTFIX = "_RESPONSE";
+    public static final int NORMAL_STATE_ORDINAL = 0;
+    public static final int SPRING_LOADED_STATE_ORDINAL = 1;
+    public static final int OVERVIEW_STATE_ORDINAL = 2;
+    public static final int ALL_APPS_STATE_ORDINAL = 3;
+    public static final int BACKGROUND_APP_STATE_ORDINAL = 4;
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 1dec173..e5ab2d1 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -52,6 +52,8 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.shortcuts.DeepShortcutView;
 import com.android.launcher3.util.IntArray;
 
 import java.io.Closeable;
@@ -65,6 +67,9 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
+import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+
 /**
  * Various utilities shared amongst the Launcher's classes.
  */
@@ -541,4 +546,48 @@
     public static String getPointString(int x, int y) {
         return String.format(Locale.ENGLISH, "%d,%d", x, y);
     }
+
+    /**
+     * Returns the location bounds of a view.
+     * - For DeepShortcutView, we return the bounds of the icon view.
+     * - For BubbleTextView, we return the icon bounds.
+     */
+    public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
+        final DragLayer dragLayer = launcher.getDragLayer();
+        final boolean isBubbleTextView = v instanceof BubbleTextView;
+        final Rect rect = new Rect();
+
+        final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
+        if (fromDeepShortcutView) {
+            // Deep shortcut views have their icon drawn in a separate view.
+            DeepShortcutView view = (DeepShortcutView) v.getParent();
+            dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
+        } else if (isBubbleTextView && v.getTag() instanceof ItemInfo
+                && (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
+                || ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
+            BubbleTextView btv = (BubbleTextView) v;
+            CellLayout pageViewIsOn = ((CellLayout) btv.getParent().getParent());
+            int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);
+
+            DeviceProfile dp = launcher.getDeviceProfile();
+            ItemInfo info = ((ItemInfo) btv.getTag());
+            dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
+                    info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
+        } else {
+            dragLayer.getDescendantRectRelativeToSelf(v, rect);
+        }
+        int viewLocationLeft = rect.left;
+        int viewLocationTop = rect.top;
+
+        if (isBubbleTextView && !fromDeepShortcutView) {
+            BubbleTextView btv = (BubbleTextView) v;
+            btv.getIconBounds(rect);
+        } else {
+            rect.set(0, 0, rect.width(), rect.height());
+        }
+        viewLocationLeft += rect.left;
+        viewLocationTop += rect.top;
+        outRect.set(viewLocationLeft, viewLocationTop, viewLocationLeft + rect.width(),
+                viewLocationTop + rect.height());
+    }
 }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 2db6cd9..7f5ca42 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -34,6 +34,7 @@
 import android.app.WallpaperManager;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -97,6 +98,7 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Objects;
 import java.util.Set;
 import java.util.function.Predicate;
 
@@ -2889,6 +2891,88 @@
         return layouts;
     }
 
+    /**
+     * Returns a list of all the CellLayouts on the Homescreen, starting with
+     * {@param startPage}, then going outward alternating between pages prior to the startPage,
+     * and then the pages after the startPage.
+     * ie. if there are 5 pages [0, 1, 2, 3, 4] and startPage is 1, we return [1, 0, 2, 3, 4].
+     */
+    private CellLayout[] getWorkspaceCellLayouts(int startPage) {
+        int screenCount = getChildCount();
+        final CellLayout[] layouts = new CellLayout[screenCount];
+        int screen = 0;
+
+        layouts[screen] = (CellLayout) getChildAt(startPage);
+        screen++;
+
+        for (int i = 1; screen < screenCount; ++i) {
+            CellLayout prevPage = (CellLayout) getChildAt(startPage - i);
+            CellLayout nextPage = (CellLayout) getChildAt(startPage + i);
+
+            if (prevPage != null) {
+                layouts[screen] = prevPage;
+                screen++;
+            }
+            if (nextPage != null) {
+                layouts[screen] = nextPage;
+                screen++;
+            }
+        }
+        return layouts;
+    }
+
+    /**
+     * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close
+     * animation.
+     *
+     * @param component The component of the task being dismissed.
+     */
+    public View getFirstMatchForAppClose(ComponentName component) {
+        final int curPage = getCurrentPage();
+        final CellLayout currentPage = (CellLayout) getPageAt(curPage);
+        final Workspace.ItemOperator isItemComponent = (info, view) ->
+                info != null && Objects.equals(info.getTargetComponent(), component);
+        final Workspace.ItemOperator isItemInFolder = (info, view) -> {
+            if (info instanceof FolderInfo) {
+                FolderInfo folderInfo = (FolderInfo) info;
+                for (ShortcutInfo shortcutInfo : folderInfo.contents) {
+                    if (Objects.equals(shortcutInfo.getTargetComponent(), component)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        };
+
+        CellLayout[] hotseatAndCurrentPage = new CellLayout[] { getHotseat(), currentPage };
+        // First we look if the app itself is in the hotseat or on the current workspace page.
+        View icon = getFirstMatch(hotseatAndCurrentPage, isItemComponent);
+        if (icon != null) {
+            return icon;
+        }
+        // Then we look if the app is in a folder on the hotseat or current workspace page.
+        icon = getFirstMatch(hotseatAndCurrentPage, isItemInFolder);
+        if (icon != null) {
+            return icon;
+        }
+        // Continue searching for the app or for a folder with the app on other pages of the
+        // workspace. We skip the current page, since we already searched above.
+        CellLayout[] allPages = getWorkspaceCellLayouts(curPage);
+        CellLayout[] page = new CellLayout[1];
+        for (int i = 1; i < allPages.length; ++i) {
+            page[0] = allPages[i];
+            icon = getFirstMatch(page, isItemComponent);
+            if (icon != null) {
+                return icon;
+            }
+            icon = getFirstMatch(page, isItemInFolder);
+            if (icon != null) {
+                return icon;
+            }
+        }
+        return null;
+    }
+
     public View getHomescreenIconByItemId(final int id) {
         return getFirstMatch((info, v) -> info != null && info.id == id);
     }
@@ -2914,6 +2998,23 @@
         return value[0];
     }
 
+    private View getFirstMatch(CellLayout[] cellLayouts, final ItemOperator operator) {
+        final View[] value = new View[1];
+        for (CellLayout cellLayout : cellLayouts) {
+            mapOverCellLayout(MAP_NO_RECURSE, cellLayout, (info, v) -> {
+                if (operator.evaluate(info, v)) {
+                    value[0] = v;
+                    return true;
+                }
+                return false;
+            });
+            if (value[0] != null) {
+                break;
+            }
+        }
+        return value[0];
+    }
+
     void clearDropTargets() {
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
@@ -2992,31 +3093,38 @@
      */
     public void mapOverItems(boolean recurse, ItemOperator op) {
         for (CellLayout layout : getWorkspaceAndHotseatCellLayouts()) {
-            ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
-            // map over all the shortcuts on the workspace
-            final int itemCount = container.getChildCount();
-            for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
-                View item = container.getChildAt(itemIdx);
-                ItemInfo info = (ItemInfo) item.getTag();
-                if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
-                    FolderIcon folder = (FolderIcon) item;
-                    ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
-                    // map over all the children in the folder
-                    final int childCount = folderChildren.size();
-                    for (int childIdx = 0; childIdx < childCount; childIdx++) {
-                        View child = folderChildren.get(childIdx);
-                        info = (ItemInfo) child.getTag();
-                        if (op.evaluate(info, child)) {
-                            return;
-                        }
+            if (mapOverCellLayout(recurse, layout, op)) {
+                return;
+            }
+        }
+    }
+
+    private boolean mapOverCellLayout(boolean recurse, CellLayout layout, ItemOperator op) {
+        ShortcutAndWidgetContainer container = layout.getShortcutsAndWidgets();
+        // map over all the shortcuts on the workspace
+        final int itemCount = container.getChildCount();
+        for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+            View item = container.getChildAt(itemIdx);
+            ItemInfo info = (ItemInfo) item.getTag();
+            if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
+                FolderIcon folder = (FolderIcon) item;
+                ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+                // map over all the children in the folder
+                final int childCount = folderChildren.size();
+                for (int childIdx = 0; childIdx < childCount; childIdx++) {
+                    View child = folderChildren.get(childIdx);
+                    info = (ItemInfo) child.getTag();
+                    if (op.evaluate(info, child)) {
+                        return true;
                     }
-                } else {
-                    if (op.evaluate(info, item)) {
-                        return;
-                    }
+                }
+            } else {
+                if (op.evaluate(info, item)) {
+                    return true;
                 }
             }
         }
+        return false;
     }
 
     void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 6feb1e9..51e914c 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -52,11 +52,14 @@
         return (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
 
-    public static void sendEventToTest(Context context, String eventTag) {
+    public static void sendStateEventToTest(Context context, int stateOrdinal) {
         final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
         if (accessibilityManager == null) return;
 
-        sendEventToTest(accessibilityManager, eventTag, null);
+        final Bundle parcel = new Bundle();
+        parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
+
+        sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
     }
 
     private static void sendEventToTest(
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index b921d29..882529d 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -88,10 +88,6 @@
     // trying to make them fit the orientation the device is in.
     public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
 
-    public static final ToggleableGlobalSettingsFlag QUICK_SWITCH
-            = new ToggleableGlobalSettingsFlag("QUICK_SWITCH", false,
-            "Swiping right on the nav bar while in an app switches to the previous app");
-
     public static final ToggleableGlobalSettingsFlag STYLE_WALLPAPER
             = new ToggleableGlobalSettingsFlag("STYLE_WALLPAPER", false,
             "Direct users to the new ThemePicker based WallpaperPicker");
@@ -102,10 +98,6 @@
     public static final TogglableFlag APPLY_CONFIG_AT_RUNTIME = new TogglableFlag(
             "APPLY_CONFIG_AT_RUNTIME", true, "Apply display changes dynamically");
 
-    public static final ToggleableGlobalSettingsFlag ENABLE_TASK_STABILIZER
-            = new ToggleableGlobalSettingsFlag("ENABLE_TASK_STABILIZER", false,
-            "Stable task list across fast task switches");
-
     public static final TogglableFlag QUICKSTEP_SPRINGS = new TogglableFlag("QUICKSTEP_SPRINGS",
             false, "Enable springs for quickstep animations");
 
@@ -116,6 +108,10 @@
             = new ToggleableGlobalSettingsFlag("SWIPE_HOME", false,
             "Swiping up on the nav bar goes home. Swipe and hold goes to recent apps.");
 
+    public static final TogglableFlag ENABLE_HINTS_IN_OVERVIEW = new TogglableFlag(
+            "ENABLE_HINTS_IN_OVERVIEW", false,
+            "Show chip hints and gleams on the overview screen");
+
     public static void initialize(Context context) {
         // Avoid the disk read for user builds
         if (Utilities.IS_DEBUG_DEVICE) {
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index ccc15f1..0d499c1 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -384,6 +384,7 @@
         openAnim.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
+                setAlpha(1f);
                 announceAccessibilityChanges();
                 mOpenCloseAnimator = null;
             }
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 0e2ed6c..c125c10 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -522,8 +522,7 @@
             }
             mLauncher.getStateManager().goToState(targetState, false /* animated */);
 
-            AccessibilityManagerCompat.sendEventToTest(
-                    mLauncher, TestProtocol.SWITCHED_TO_STATE_MESSAGE);
+            AccessibilityManagerCompat.sendStateEventToTest(mLauncher, targetState.ordinal);
         }
     }
 
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
new file mode 100644
index 0000000..07318c9
--- /dev/null
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -0,0 +1,138 @@
+/*
+ * 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.views;
+
+import android.animation.Animator;
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.InsettableFrameLayout.LayoutParams;
+import com.android.launcher3.ItemInfoWithIcon;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.graphics.DrawableFactory;
+
+/**
+ * A view that is created to look like another view with the purpose of creating fluid animations.
+ */
+public class FloatingIconView extends View implements Animator.AnimatorListener {
+
+    private Runnable mStartRunnable;
+    private Runnable mEndRunnable;
+
+    public FloatingIconView(Context context) {
+        super(context);
+    }
+
+    public void setRunnables(Runnable startRunnable, Runnable endRunnable) {
+        mStartRunnable = startRunnable;
+        mEndRunnable = endRunnable;
+    }
+
+    /**
+     * Positions this view to match the size and location of {@param rect}.
+     */
+    public void update(RectF rect, float alpha) {
+        setAlpha(alpha);
+
+        LayoutParams lp = (LayoutParams) getLayoutParams();
+        float dX = rect.left - lp.leftMargin;
+        float dY = rect.top - lp.topMargin;
+        setTranslationX(dX);
+        setTranslationY(dY);
+
+        float scaleX = rect.width() / (float) getWidth();
+        float scaleY = rect.height() / (float) getHeight();
+        float scale = Math.min(scaleX, scaleY);
+        setPivotX(0);
+        setPivotY(0);
+        setScaleX(scale);
+        setScaleY(scale);
+    }
+
+    @Override
+    public void onAnimationStart(Animator animator) {
+        if (mStartRunnable != null) {
+            mStartRunnable.run();
+        }
+    }
+
+    @Override
+    public void onAnimationEnd(Animator animator) {
+        if (mEndRunnable != null) {
+            mEndRunnable.run();
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(Animator animator) {
+    }
+
+    @Override
+    public void onAnimationRepeat(Animator animator) {
+    }
+
+    /**
+     * Sets the size and position of this view to match {@param v}.
+     *
+     * @param v The view to copy
+     * @param hideOriginal If true, it will hide {@param v} while this view is visible.
+     * @param positionOut Rect that will hold the size and position of v.
+     */
+    public void matchPositionOf(Launcher launcher, View v, boolean hideOriginal, Rect positionOut) {
+        Utilities.getLocationBoundsForView(launcher, v, positionOut);
+        final LayoutParams lp = new LayoutParams(positionOut.width(), positionOut.height());
+        lp.ignoreInsets = true;
+
+        // Position the floating view exactly on top of the original
+        lp.leftMargin = positionOut.left;
+        lp.topMargin = positionOut.top;
+        setLayoutParams(lp);
+        // Set the properties here already to make sure they are available when running the first
+        // animation frame.
+        layout(lp.leftMargin, lp.topMargin, lp.leftMargin + lp.width, lp.topMargin
+                + lp.height);
+
+        if (v instanceof BubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) {
+            // Create a copy of the app icon
+            setBackground(DrawableFactory.INSTANCE.get(launcher)
+                    .newIcon(v.getContext(), (ItemInfoWithIcon) v.getTag()));
+        }
+
+        // We need to add it to the overlay, but keep it invisible until animation starts..
+        final DragLayer dragLayer = launcher.getDragLayer();
+        setVisibility(INVISIBLE);
+        ((ViewGroup) dragLayer.getParent()).getOverlay().add(this);
+
+        setRunnables(() -> {
+                    setVisibility(VISIBLE);
+                    if (hideOriginal) {
+                        v.setVisibility(INVISIBLE);
+                    }
+                },
+                () -> {
+                    ((ViewGroup) dragLayer.getParent()).getOverlay().remove(this);
+                    if (hideOriginal) {
+                        v.setVisibility(VISIBLE);
+                    }
+                });
+    }
+}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java
deleted file mode 100644
index 147d194..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/FastOverviewState.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.launcher3.uioverrides;
-
-/**
- * A dummy overview state
- */
-public class FastOverviewState extends OverviewState {
-
-    public FastOverviewState(int id) {
-        super(id);
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java b/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
deleted file mode 100644
index fdf87be..0000000
--- a/tests/src/com/android/launcher3/ui/AllAppsIconToHomeTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.pm.LauncherActivityInfo;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for dragging an icon from all-apps to homescreen.
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class AllAppsIconToHomeTest extends AbstractLauncherUiTest {
-
-    @Test
-    @PortraitLandscape
-    public void testDragIcon() throws Throwable {
-        LauncherActivityInfo settingsApp = getSettingsApp();
-
-        clearHomescreen();
-
-        final String appName = settingsApp.getLabel().toString();
-        // 1. Open all apps and wait for load complete.
-        // 2. Drag icon to homescreen.
-        // 3. Verify that the icon works on homescreen.
-        mLauncher.pressHome().
-                switchToAllApps().
-                getAppIcon(appName).
-                dragToWorkspace().
-                getWorkspaceAppIcon(appName).
-                launch(settingsApp.getComponentName().getPackageName());
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java b/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
deleted file mode 100644
index 1fea4d5..0000000
--- a/tests/src/com/android/launcher3/ui/ShortcutsLaunchTest.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package com.android.launcher3.ui;
-
-import static org.junit.Assert.assertTrue;
-
-import android.content.pm.LauncherActivityInfo;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.popup.ArrowPopup;
-import com.android.launcher3.tapl.AppIconMenu;
-import com.android.launcher3.tapl.AppIconMenuItem;
-import com.android.launcher3.views.OptionsPopupView;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for verifying that shortcuts are shown and can be launched after long pressing an app
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ShortcutsLaunchTest extends AbstractLauncherUiTest {
-
-    private boolean isOptionsPopupVisible(Launcher launcher) {
-        final ArrowPopup popup = OptionsPopupView.getOptionsPopup(launcher);
-        return popup != null && popup.isShown();
-    }
-
-    @Test
-    @PortraitLandscape
-    public void testAppLauncher() throws Exception {
-        mActivityMonitor.startLauncher();
-        final LauncherActivityInfo testApp = getSettingsApp();
-
-        final AppIconMenu menu = mLauncher.
-                pressHome().
-                switchToAllApps().
-                getAppIcon(testApp.getLabel().toString()).
-                openMenu();
-
-        executeOnLauncher(
-                launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
-                        isOptionsPopupVisible(launcher)));
-
-        final AppIconMenuItem menuItem = menu.getMenuItem(1);
-        final String itemName = menuItem.getText();
-
-        menuItem.launch(testApp.getComponentName().getPackageName(), itemName);
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java b/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
deleted file mode 100644
index 4c2c959..0000000
--- a/tests/src/com/android/launcher3/ui/ShortcutsToHomeTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package com.android.launcher3.ui;
-
-import android.content.pm.LauncherActivityInfo;
-
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.launcher3.tapl.AppIconMenuItem;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for dragging a deep shortcut to the home screen.
- */
-@LargeTest
-@RunWith(AndroidJUnit4.class)
-public class ShortcutsToHomeTest extends AbstractLauncherUiTest {
-
-    @Test
-    @PortraitLandscape
-    public void testDragIcon() throws Throwable {
-        clearHomescreen();
-        mActivityMonitor.startLauncher();
-
-        LauncherActivityInfo testApp = getSettingsApp();
-
-        // 1. Open all apps and wait for load complete.
-        // 2. Find the app and long press it to show shortcuts.
-        // 3. Press icon center until shortcuts appear
-        final AppIconMenuItem menuItem = mLauncher.
-                getWorkspace().
-                switchToAllApps().
-                getAppIcon(testApp.getLabel().toString()).
-                openMenu().
-                getMenuItem(0);
-        final String shortcutName = menuItem.getText();
-
-        // 4. Drag the first shortcut to the home screen.
-        // 5. Verify that the shortcut works on home screen
-        //    (the app opens and has the same text as the shortcut).
-        menuItem.
-                dragToWorkspace().
-                getWorkspaceAppIcon(shortcutName).
-                launch(testApp.getComponentName().getPackageName(), shortcutName);
-    }
-}
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index ab5761d..6f2f280 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
 import android.util.Log;
 
 import androidx.test.filters.LargeTest;
@@ -33,8 +34,12 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.tapl.AllApps;
 import com.android.launcher3.tapl.AppIcon;
+import com.android.launcher3.tapl.AppIconMenu;
+import com.android.launcher3.tapl.AppIconMenuItem;
+import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.tapl.Widgets;
 import com.android.launcher3.tapl.Workspace;
 import com.android.launcher3.views.OptionsPopupView;
@@ -100,12 +105,19 @@
     @Before
     public void setUp() throws Exception {
         super.setUp();
+        initialize(this);
+    }
 
-        clearLauncherData();
-
-        mLauncher.pressHome();
-        waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
-        waitForResumed("Launcher internal state is still Background");
+    public static void initialize(AbstractLauncherUiTest test) throws Exception {
+        test.clearLauncherData();
+        if (TestHelpers.isInLauncherProcess()) {
+            test.mActivityMonitor.returnToHome();
+        } else {
+            test.mDevice.pressHome();
+        }
+        test.waitForLauncherCondition("Launcher didn't start", launcher -> launcher != null);
+        test.waitForState("Launcher internal state didn't switch to Home", LauncherState.NORMAL);
+        test.waitForResumed("Launcher internal state is still Background");
     }
 
     // Please don't add negative test cases for methods that fail only after a long wait.
@@ -284,4 +296,74 @@
     private int getWidgetsScroll(Launcher launcher) {
         return getWidgetsView(launcher).getCurrentScrollY();
     }
+
+    private boolean isOptionsPopupVisible(Launcher launcher) {
+        final ArrowPopup popup = OptionsPopupView.getOptionsPopup(launcher);
+        return popup != null && popup.isShown();
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testLaunchMenuItem() throws Exception {
+        if (!TestHelpers.isInLauncherProcess()) return;
+        final LauncherActivityInfo testApp = getSettingsApp();
+
+        final AppIconMenu menu = mLauncher.
+                getWorkspace().
+                switchToAllApps().
+                getAppIcon(testApp.getLabel().toString()).
+                openMenu();
+
+        executeOnLauncher(
+                launcher -> assertTrue("Launcher internal state didn't switch to Showing Menu",
+                        isOptionsPopupVisible(launcher)));
+
+        final AppIconMenuItem menuItem = menu.getMenuItem(1);
+        final String itemName = menuItem.getText();
+
+        menuItem.launch(testApp.getComponentName().getPackageName(), itemName);
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testDragAppIcon() throws Throwable {
+        LauncherActivityInfo settingsApp = getSettingsApp();
+
+        final String appName = settingsApp.getLabel().toString();
+        // 1. Open all apps and wait for load complete.
+        // 2. Drag icon to homescreen.
+        // 3. Verify that the icon works on homescreen.
+        mLauncher.getWorkspace().
+                switchToAllApps().
+                getAppIcon(appName).
+                dragToWorkspace().
+                getWorkspaceAppIcon(appName).
+                launch(settingsApp.getComponentName().getPackageName());
+    }
+
+    @Test
+    @PortraitLandscape
+    public void testDragShortcut() throws Throwable {
+        if (!TestHelpers.isInLauncherProcess()) return;
+        LauncherActivityInfo testApp = getSettingsApp();
+
+        // 1. Open all apps and wait for load complete.
+        // 2. Find the app and long press it to show shortcuts.
+        // 3. Press icon center until shortcuts appear
+        final AppIconMenuItem menuItem = mLauncher.
+                getWorkspace().
+                switchToAllApps().
+                getAppIcon(testApp.getLabel().toString()).
+                openMenu().
+                getMenuItem(0);
+        final String shortcutName = menuItem.getText();
+
+        // 4. Drag the first shortcut to the home screen.
+        // 5. Verify that the shortcut works on home screen
+        //    (the app opens and has the same text as the shortcut).
+        menuItem.
+                dragToWorkspace().
+                getWorkspaceAppIcon(shortcutName).
+                launch(testApp.getComponentName().getPackageName(), shortcutName);
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
index 42817c1..2642815 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.TestProtocol.OVERVIEW_STATE_ORDINAL;
+
 import android.graphics.Point;
 
 import androidx.annotation.NonNull;
@@ -45,7 +47,7 @@
         final Point start = qsb.getVisibleCenter();
         final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.6);
         LauncherInstrumentation.log("AllAppsFromOverview.switchBackToOverview before swipe");
-        mLauncher.swipe(start.x, start.y, start.x, endY);
+        mLauncher.swipe(start.x, start.y, start.x, endY, OVERVIEW_STATE_ORDINAL);
 
         return new Overview(mLauncher);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 08d2889..606cf37 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
 import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
 
@@ -50,23 +51,28 @@
     @NonNull
     public BaseOverview switchToOverview() {
         verifyActiveContainer();
-        goToOverviewUnchecked();
+        goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
         assertTrue("Overview not visible", mLauncher.getDevice().wait(
                 Until.hasObject(By.pkg(getOverviewPackageName())), WAIT_TIME_MS));
         return new BaseOverview(mLauncher);
     }
 
-
-    protected void goToOverviewUnchecked() {
+    protected void goToOverviewUnchecked(int expectedState) {
         if (mLauncher.isSwipeUpEnabled()) {
             final int height = mLauncher.getDevice().getDisplayHeight();
             final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame");
 
+            int swipeLength = Math.round(getSwipeLength() * mLauncher.getDisplayDensity());
             mLauncher.swipe(
                     navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
-                    navBar.getVisibleBounds().centerX(), height - 400);
+                    navBar.getVisibleBounds().centerX(), height - swipeLength,
+                    expectedState);
         } else {
             mLauncher.getSystemUiObject("recent_apps").click();
         }
     }
+
+    protected int getSwipeLength() {
+        return 200;
+    }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index 522ce14..f8bd85a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.TestProtocol.OVERVIEW_STATE_ORDINAL;
+
 import androidx.annotation.NonNull;
 
 /**
@@ -47,7 +49,7 @@
     @Override
     public Overview switchToOverview() {
         verifyActiveContainer();
-        goToOverviewUnchecked();
+        goToOverviewUnchecked(OVERVIEW_STATE_ORDINAL);
         return new Overview(mLauncher);
     }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 7e2c966..481281a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -74,8 +74,11 @@
      */
     public Workspace dragToWorkspace() {
         final UiDevice device = mLauncher.getDevice();
-        mObject.drag(new Point(
-                device.getDisplayWidth() / 2, device.getDisplayHeight() / 2), DRAG_SPEED);
+        Workspace.dragIconToWorkspace(
+                mLauncher,
+                this,
+                new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2),
+                DRAG_SPEED);
         return new Workspace(mLauncher);
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index acdcd75..7473189 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -104,13 +104,13 @@
     public LauncherInstrumentation(Instrumentation instrumentation) {
         mInstrumentation = instrumentation;
         mDevice = UiDevice.getInstance(instrumentation);
-        final boolean swipeUpEnabledDefault =
-                !SwipeUpSetting.isSwipeUpSettingAvailable() ||
-                        SwipeUpSetting.isSwipeUpEnabledDefaultValue();
-        mSwipeUpEnabled = Settings.Secure.getInt(
-                instrumentation.getTargetContext().getContentResolver(),
-                SWIPE_UP_SETTING_NAME,
-                swipeUpEnabledDefault ? 1 : 0) == 1;
+        final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
+        mSwipeUpEnabled = SwipeUpSetting.isSwipeUpSettingAvailable() ?
+                Settings.Secure.getInt(
+                        instrumentation.getTargetContext().getContentResolver(),
+                        SWIPE_UP_SETTING_NAME,
+                        swipeUpEnabledDefaultValue ? 1 : 0) == 1 :
+                swipeUpEnabledDefaultValue;
 
         // Launcher should run in test harness so that custom accessibility protocol between
         // Launcher and TAPL is enabled. In-process tests enable this protocol with a direct call
@@ -154,7 +154,7 @@
         fail(message + ". " + "Actual: " + actual);
     }
 
-    static public void assertEquals(String message, int expected, int actual) {
+    static private void assertEquals(String message, int expected, int actual) {
         if (expected != actual) {
             fail(message + " expected: " + expected + " but was: " + actual);
         }
@@ -412,16 +412,18 @@
     }
 
     @NonNull
-    UiDevice getDevice() {
+    public UiDevice getDevice() {
         return mDevice;
     }
 
-    void swipe(int startX, int startY, int endX, int endY) {
-        executeAndWaitForEvent(
+    void swipe(int startX, int startY, int endX, int endY, int expectedState) {
+        final Bundle parcel = (Bundle) executeAndWaitForEvent(
                 () -> mDevice.swipe(startX, startY, endX, endY, 60),
                 event -> TestProtocol.SWITCHED_TO_STATE_MESSAGE.equals(event.getClassName()),
                 "Swipe failed to receive an event for the swipe end: " + startX + ", " + startY
                         + ", " + endX + ", " + endY);
+        assertEquals("Swipe switched launcher to a wrong state",
+                expectedState, parcel.getInt(TestProtocol.STATE_FIELD));
     }
 
     void waitForIdle() {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 9e0c07f..0208144 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.TestProtocol.ALL_APPS_STATE_ORDINAL;
+
 import android.graphics.Point;
 
 import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
@@ -51,7 +53,7 @@
         final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame");
         final Point start = navBar.getVisibleCenter();
         LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
-        mLauncher.swipe(start.x, start.y, start.x, 0);
+        mLauncher.swipe(start.x, start.y, start.x, 0, ALL_APPS_STATE_ORDINAL);
 
         return new AllAppsFromOverview(mLauncher);
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index 48686c4..7ccd49b 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -24,6 +24,7 @@
  * A recent task in the overview panel carousel.
  */
 public final class OverviewTask {
+    static final int FLING_SPEED = 3000;
     private final LauncherInstrumentation mLauncher;
     private final UiObject2 mTask;
     private final BaseOverview mOverview;
@@ -45,7 +46,7 @@
     public void dismiss() {
         verifyActiveContainer();
         // Dismiss the task via flinging it up.
-        mTask.fling(Direction.DOWN);
+        mTask.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
         mLauncher.waitForIdle();
     }
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 587c712..5cd41f9 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3.tapl;
 
+import static com.android.launcher3.TestProtocol.ALL_APPS_STATE_ORDINAL;
+
 import static junit.framework.TestCase.assertTrue;
 
 import android.graphics.Point;
@@ -55,7 +57,8 @@
                 start.x,
                 start.y,
                 start.x,
-                endY
+                endY,
+                ALL_APPS_STATE_ORDINAL
         );
 
         return new AllApps(mLauncher);
@@ -96,7 +99,13 @@
     public void ensureWorkspaceIsScrollable() {
         final UiObject2 workspace = verifyActiveContainer();
         if (!isWorkspaceScrollable(workspace)) {
-            dragIconToNextScreen(getHotseatAppIcon("Messages"), workspace);
+            dragIconToWorkspace(
+                    mLauncher,
+                    getHotseatAppIcon("Messages"),
+                    new Point(mLauncher.getDevice().getDisplayWidth(),
+                            workspace.getVisibleBounds().centerY()),
+                    ICON_DRAG_SPEED);
+            verifyActiveContainer();
         }
         assertTrue("Home screen workspace didn't become scrollable",
                 isWorkspaceScrollable(workspace));
@@ -112,12 +121,10 @@
                 mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
     }
 
-    private void dragIconToNextScreen(AppIcon app, UiObject2 workspace) {
-        final Point dest = new Point(
-                mLauncher.getDevice().getDisplayWidth(), workspace.getVisibleBounds().centerY());
-        app.getObject().drag(dest, ICON_DRAG_SPEED);
-        mLauncher.waitUntilGone("drop_target_bar");
-        verifyActiveContainer();
+    static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
+            Point dest, int icon_drag_speed) {
+        launchable.getObject().drag(dest, icon_drag_speed);
+        launcher.waitUntilGone("drop_target_bar");
     }
 
     /**
@@ -153,4 +160,9 @@
         mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
         return new Widgets(mLauncher);
     }
+
+    @Override
+    protected int getSwipeLength() {
+        return 100;
+    }
 }
\ No newline at end of file