Merge "Add clear all animation to Recents Go." into ub-launcher3-master
diff --git a/Android.mk b/Android.mk
index b3b1c82..06ee66f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,10 +33,7 @@
 LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE_TAGS := optional
 
-ifneq (,$(wildcard frameworks/base))
-else
-    LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore
-endif
+LOCAL_STATIC_JAVA_LIBRARIES:= libPluginCore
 
 LOCAL_SRC_FILES := \
     $(call all-java-files-under, src_plugins)
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/go/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
deleted file mode 100644
index 4038c99..0000000
--- a/go/quickstep/src/com/android/launcher3/uioverrides/BackgroundAppState.java
+++ /dev/null
@@ -1,26 +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.launcher3.uioverrides;
-
-/**
- * State indicating that the Launcher is behind an app. Same as {@link OverviewState} for Go as we
- * do not support swipe to overview or swipe to home.
- */
-public final class BackgroundAppState extends OverviewState {
-    public BackgroundAppState(int id) {
-        super(id);
-    }
-}
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
index 29e650c..321861b 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsUiFactory.java
@@ -17,7 +17,6 @@
 package com.android.launcher3.uioverrides;
 
 import static android.view.View.VISIBLE;
-
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 
 import android.view.View;
@@ -26,6 +25,10 @@
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
 import com.android.launcher3.util.TouchController;
 import com.android.quickstep.OverviewInteractionState;
 import com.android.quickstep.views.IconRecentsView;
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
similarity index 85%
rename from go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
rename to go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index cec12a8..6730e97 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
@@ -43,8 +43,8 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
-        return new float[] {1f, 0f};
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1f, 0f, 0f);
     }
 
     @Override
@@ -91,4 +91,17 @@
     public static float getDefaultSwipeHeight(DeviceProfile dp) {
         return dp.allAppsCellHeightPx - dp.allAppsIconTextSizePx;
     }
+
+
+    public static OverviewState newBackgroundState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newPeekState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newSwitchState(int id) {
+        return new OverviewState(id);
+    }
 }
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
similarity index 97%
rename from go/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
rename to go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
index 2c91bc3..1ccd7d7 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeStatesTouchController.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
similarity index 97%
rename from go/quickstep/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
rename to go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index a3b41b0..011a4e7 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import android.view.MotionEvent;
 
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index e56cc51..c98eca6 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -75,8 +75,20 @@
             // Task list has updated.
             return;
         }
-        holder.bindTask(tasks.get(position));
-
+        Task task = tasks.get(position);
+        holder.bindTask(task);
+        mLoader.loadTaskIconAndLabel(task, () -> {
+            // Ensure holder still has the same task.
+            if (task.equals(holder.getTask())) {
+                holder.getTaskItemView().setIcon(task.icon);
+                holder.getTaskItemView().setLabel(task.titleDescription);
+            }
+        });
+        mLoader.loadTaskThumbnail(task, () -> {
+            if (task.equals(holder.getTask())) {
+                holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
+            }
+        });
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/TaskHolder.java b/go/quickstep/src/com/android/quickstep/TaskHolder.java
index a89229f..744afd7 100644
--- a/go/quickstep/src/com/android/quickstep/TaskHolder.java
+++ b/go/quickstep/src/com/android/quickstep/TaskHolder.java
@@ -35,17 +35,18 @@
         mTaskItemView = itemView;
     }
 
+    public TaskItemView getTaskItemView() {
+        return mTaskItemView;
+    }
+
     /**
-     * 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.
+     * Bind a task to the holder, resetting the view and preparing it for content to load in.
      *
      * @param task the task to bind to the view
      */
     public void bindTask(Task task) {
         mTask = task;
-        mTaskItemView.setLabel(task.titleDescription);
-        mTaskItemView.setIcon(task.icon);
-        mTaskItemView.setThumbnail(task.thumbnail.thumbnail);
+        mTaskItemView.resetTaskItemView();
     }
 
     /**
diff --git a/go/quickstep/src/com/android/quickstep/TaskListLoader.java b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
index c86c24e..1234989 100644
--- a/go/quickstep/src/com/android/quickstep/TaskListLoader.java
+++ b/go/quickstep/src/com/android/quickstep/TaskListLoader.java
@@ -25,7 +25,6 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 /**
@@ -39,35 +38,48 @@
 
     private ArrayList<Task> mTaskList = new ArrayList<>();
     private int mTaskListChangeId;
+    private RecentsModel.TaskThumbnailChangeListener listener = (taskId, thumbnailData) -> {
+        Task foundTask = null;
+        for (Task task : mTaskList) {
+            if (task.key.id == taskId) {
+                foundTask = task;
+                break;
+            }
+        }
+        if (foundTask != null) {
+            foundTask.thumbnail = thumbnailData;
+        }
+        return foundTask;
+    };
 
     public TaskListLoader(Context context) {
         mRecentsModel = RecentsModel.INSTANCE.get(context);
+        mRecentsModel.addThumbnailChangeListener(listener);
     }
 
     /**
-     * Returns the current task list as of the last completed load (see
-     * {@link #loadTaskList}) as a read-only list. This list of tasks is guaranteed to always have
-     * all its task content loaded.
+     * Returns the current task list as of the last completed load (see {@link #loadTaskList}) as a
+     * read-only list. This list of tasks is not guaranteed to have all content loaded.
      *
-     * @return the current list of tasks w/ all content loaded
+     * @return the current list of tasks
      */
     public List<Task> getCurrentTaskList() {
         return Collections.unmodifiableList(mTaskList);
     }
 
     /**
-     * Fetches the most recent tasks and updates the task list asynchronously. In addition it
-     * loads the content for each task (icon and label). The callback and task list being updated
-     * only occur when all task content is fully loaded and up-to-date.
+     * Fetches the most recent tasks and updates the task list asynchronously. This call does not
+     * provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
+     * what it has. May run the callback immediately if there have been no changes in the task
+     * list.
      *
-     * @param onTasksLoadedCallback callback for when the tasks are fully loaded. Done on the UI
-     *                              thread
+     * @param onLoadedCallback callback to run when task list is loaded
      */
-    public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onTasksLoadedCallback) {
+    public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
         if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
             // Current task list is already up to date. No need to update.
-            if (onTasksLoadedCallback != null) {
-                onTasksLoadedCallback.accept(mTaskList);
+            if (onLoadedCallback != null) {
+                onLoadedCallback.accept(mTaskList);
             }
             return;
         }
@@ -76,16 +88,46 @@
             // Reverse tasks to put most recent at the bottom of the view
             Collections.reverse(tasks);
             // Load task content
-            loadTaskContents(tasks, () -> {
-                mTaskList = tasks;
-                if (onTasksLoadedCallback != null) {
-                    onTasksLoadedCallback.accept(mTaskList);
+            for (Task task : tasks) {
+                int loadedPos = mTaskList.indexOf(task);
+                if (loadedPos == -1) {
+                    continue;
                 }
-            });
+                Task loadedTask = mTaskList.get(loadedPos);
+                task.icon = loadedTask.icon;
+                task.titleDescription = loadedTask.titleDescription;
+                task.thumbnail = loadedTask.thumbnail;
+            }
+            mTaskList = tasks;
+            onLoadedCallback.accept(tasks);
         });
     }
 
     /**
+     * Load task icon and label asynchronously if it is not already loaded in the task. If the task
+     * already has an icon, this calls the callback immediately.
+     *
+     * @param task task to update with icon + label
+     * @param onLoadedCallback callback to run when task has icon and label
+     */
+    public void loadTaskIconAndLabel(Task task, @Nullable Runnable onLoadedCallback) {
+        mRecentsModel.getIconCache().updateIconInBackground(task,
+                loadedTask -> onLoadedCallback.run());
+    }
+
+    /**
+     * Load thumbnail asynchronously if not already loaded in the task. If the task already has a
+     * thumbnail or if the thumbnail is cached, this calls the callback immediately.
+     *
+     * @param task task to update with the thumbnail
+     * @param onLoadedCallback callback to run when task has thumbnail
+     */
+    public void loadTaskThumbnail(Task task, @Nullable Runnable onLoadedCallback) {
+        mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
+                thumbnail -> onLoadedCallback.run());
+    }
+
+    /**
      * Removes the task from the current task list.
      */
     void removeTask(Task task) {
@@ -98,42 +140,4 @@
     void clearAllTasks() {
         mTaskList.clear();
     }
-
-    /**
-     * Loads task content for a list of tasks, including the label, icon, and thumbnail. For content
-     * that isn't cached, load the content asynchronously in the background.
-     *
-     * @param tasksToLoad list of tasks that need to load their content
-     * @param onFullyLoadedCallback runnable to run after all tasks have loaded their content
-     */
-    private void loadTaskContents(ArrayList<Task> tasksToLoad,
-            @Nullable Runnable onFullyLoadedCallback) {
-        // Make two load requests per task, one for the icon/title and one for the thumbnail.
-        AtomicInteger loadRequestsCount = new AtomicInteger(tasksToLoad.size() * 2);
-        Runnable itemLoadedRunnable = () -> {
-            if (loadRequestsCount.decrementAndGet() == 0 && onFullyLoadedCallback != null) {
-                onFullyLoadedCallback.run();
-            }
-        };
-        for (Task task : tasksToLoad) {
-            // Load icon and title.
-            int index = mTaskList.indexOf(task);
-            if (index >= 0) {
-                // If we've already loaded the task and have its content then just copy it over.
-                Task loadedTask = mTaskList.get(index);
-                task.titleDescription = loadedTask.titleDescription;
-                task.icon = loadedTask.icon;
-                itemLoadedRunnable.run();
-            } else {
-                // Otherwise, load the content in the background.
-                mRecentsModel.getIconCache().updateIconInBackground(task,
-                        loadedTask -> itemLoadedRunnable.run());
-            }
-
-            // Load the thumbnail. May return immediately and synchronously if the thumbnail is
-            // cached.
-            mRecentsModel.getThumbnailCache().updateThumbnailInBackground(task,
-                    thumbnail -> itemLoadedRunnable.run());
-        }
-    }
 }
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 373f107..d831b20 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -17,6 +17,7 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
@@ -24,6 +25,8 @@
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.R;
 
 /**
@@ -31,12 +34,16 @@
  */
 public final class TaskItemView extends LinearLayout {
 
+    private static final String DEFAULT_LABEL = "...";
+    private final Drawable mDefaultIcon;
     private TextView mLabelView;
     private ImageView mIconView;
     private ImageView mThumbnailView;
 
     public TaskItemView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mDefaultIcon = context.getResources().getDrawable(
+                android.R.drawable.sym_def_app_icon, context.getTheme());
     }
 
     @Override
@@ -48,33 +55,56 @@
     }
 
     /**
-     * Set the label for the task item.
+     * Resets task item view to default values.
+     */
+    public void resetTaskItemView() {
+        setLabel(DEFAULT_LABEL);
+        setIcon(null);
+        setThumbnail(null);
+    }
+
+    /**
+     * Set the label for the task item. Sets to a default label if null.
      *
      * @param label task label
      */
-    public void setLabel(String label) {
+    public void setLabel(@Nullable String label) {
+        if (label == null) {
+            mLabelView.setText(DEFAULT_LABEL);
+            return;
+        }
         mLabelView.setText(label);
     }
 
     /**
-     * Set the icon for the task item.
+     * Set the icon for the task item. Sets to a default icon if null.
      *
      * @param icon task icon
      */
-    public void setIcon(Drawable icon) {
+    public void setIcon(@Nullable Drawable icon) {
         // TODO: Scale the icon up based off the padding on the side
         // The icon proper is actually smaller than the drawable and has "padding" on the side for
         // the purpose of drawing the shadow, allowing the icon to pop up, so we need to scale the
         // view if we want the icon to be flush with the bottom of the thumbnail.
+        if (icon == null) {
+            mIconView.setImageDrawable(mDefaultIcon);
+            return;
+        }
         mIconView.setImageDrawable(icon);
     }
 
     /**
-     * Set the task thumbnail for the task.
+     * Set the task thumbnail for the task. Sets to a default thumbnail if null.
      *
      * @param thumbnail task thumbnail for the task
      */
-    public void setThumbnail(Bitmap thumbnail) {
+    public void setThumbnail(@Nullable Bitmap thumbnail) {
+        if (thumbnail == null) {
+            mThumbnailView.setImageBitmap(null);
+            mThumbnailView.setBackgroundColor(Color.GRAY);
+            return;
+        }
+        mThumbnailView.setBackgroundColor(Color.TRANSPARENT);
         mThumbnailView.setImageBitmap(thumbnail);
     }
 
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
deleted file mode 100644
index a41362f..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/FlingAndHoldTouchController.java
+++ /dev/null
@@ -1,96 +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.launcher3.uioverrides;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
-
-import android.animation.ValueAnimator;
-
-import com.android.launcher3.Launcher;
-import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.quickstep.util.MotionPauseDetector;
-import com.android.quickstep.views.RecentsView;
-
-/**
- * Touch controller which handles swipe and hold to go to Overview
- */
-public class FlingAndHoldTouchController extends PortraitStatesTouchController {
-
-    private final MotionPauseDetector mMotionPauseDetector;
-
-    public FlingAndHoldTouchController(Launcher l) {
-        super(l, false /* allowDragToOverview */);
-        mMotionPauseDetector = new MotionPauseDetector(l);
-    }
-
-    @Override
-    public void onDragStart(boolean start) {
-        mMotionPauseDetector.clear();
-
-        super.onDragStart(start);
-
-        if (mStartState == NORMAL) {
-            mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
-                RecentsView recentsView = mLauncher.getOverviewPanel();
-                recentsView.setOverviewStateEnabled(isPaused);
-                maybeUpdateAtomicAnim(NORMAL, OVERVIEW, isPaused ? 1 : 0);
-            });
-        }
-    }
-
-    @Override
-    public boolean onDrag(float displacement) {
-        mMotionPauseDetector.addPosition(displacement, 0);
-        return super.onDrag(displacement);
-    }
-
-    @Override
-    public void onDragEnd(float velocity, boolean fling) {
-        if (mMotionPauseDetector.isPaused() && mStartState == NORMAL) {
-            float range = getShiftRange();
-            long maxAccuracy = (long) (2 * range);
-
-            // Let the state manager know that the animation didn't go to the target state,
-            // but don't cancel ourselves (we already clean up when the animation completes).
-            Runnable onCancel = mCurrentAnimation.getOnCancelRunnable();
-            mCurrentAnimation.setOnCancelRunnable(null);
-            mCurrentAnimation.dispatchOnCancel();
-            mCurrentAnimation = mLauncher.getStateManager()
-                    .createAnimationToNewWorkspace(OVERVIEW, new AnimatorSetBuilder(), maxAccuracy,
-                            onCancel, NON_ATOMIC_COMPONENT);
-
-            final int logAction = fling ? Touch.FLING : Touch.SWIPE;
-            mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(OVERVIEW, logAction));
-
-
-            ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
-            maybeUpdateAtomicAnim(NORMAL, OVERVIEW, 1f);
-            mCurrentAnimation.dispatchOnStartWithVelocity(1, velocity);
-
-            // TODO: Find a better duration
-            anim.setDuration(100);
-            anim.start();
-            settleAtomicAnimation(1f, anim.getDuration());
-        } else {
-            super.onDragEnd(velocity, fling);
-        }
-        mMotionPauseDetector.clear();
-    }
-}
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 81ff0c7..d0a2f27 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
@@ -17,7 +17,6 @@
 package com.android.launcher3.uioverrides;
 
 import static android.view.View.VISIBLE;
-
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -30,6 +29,13 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
+import com.android.launcher3.uioverrides.touchcontrollers.OverviewToAllAppsTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.QuickSwitchTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.launcher3.util.TouchController;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.launcher3.util.UiThreadHelper.AsyncCommand;
@@ -61,6 +67,7 @@
         list.add(launcher.getDragController());
 
         if (swipeUpToHome) {
+            list.add(new QuickSwitchTouchController(launcher));
             list.add(new FlingAndHoldTouchController(launcher));
         } else {
             if (launcher.getDeviceProfile().isVerticalBarLayout()) {
@@ -69,6 +76,9 @@
             } else {
                 list.add(new PortraitStatesTouchController(launcher,
                         swipeUpEnabled /* allowDragToOverview */));
+                if (swipeUpEnabled) {
+                    list.add(new QuickSwitchTouchController(launcher));
+                }
             }
         }
 
@@ -98,6 +108,10 @@
      * @param launcher the launcher activity
      */
     public static void prepareToShowOverview(Launcher launcher) {
+        if (FeatureFlags.SWIPE_HOME.get()) {
+            // Overview lives on the side, so doesn't scale in from above.
+            return;
+        }
         RecentsView overview = launcher.getOverviewPanel();
         if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) {
             SCALE_PROPERTY.set(overview, RECENTS_PREPARE_SCALE);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
similarity index 94%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
index f712753..d8f1628 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/BackgroundAppState.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 
@@ -52,7 +52,7 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
         // Initialize the recents view scale to what it would be when starting swipe up
         RecentsView recentsView = launcher.getOverviewPanel();
         recentsView.getTaskSize(sTempRect);
@@ -71,7 +71,7 @@
             }
         }
         float scale = (float) appWidth / sTempRect.width();
-        return new float[] { scale, 0f };
+        return new ScaleAndTranslation(scale, 0f, 0f);
     }
 
     @Override
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
new file mode 100644
index 0000000..bc1d4ba
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewPeekState.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides.states;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+
+public class OverviewPeekState extends OverviewState {
+    public OverviewPeekState(int id) {
+        super(id);
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        ScaleAndTranslation result = super.getOverviewScaleAndTranslation(launcher);
+        result.translationX = NORMAL.getOverviewScaleAndTranslation(launcher).translationX
+                - launcher.getResources().getDimension(R.dimen.overview_peek_distance);
+        return result;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
similarity index 84%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 2360eeb..94c1545 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -13,11 +13,12 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.states;
 
 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;
+import static com.android.launcher3.logging.LoggerUtils.getTargetStr;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
 import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
 
@@ -52,11 +53,15 @@
     }
 
     protected OverviewState(int id, int transitionDuration, int stateFlags) {
-        super(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
+        this(id, ContainerType.TASKSWITCHER, transitionDuration, stateFlags);
+    }
+
+    protected OverviewState(int id, int logContainer, int transitionDuration, int stateFlags) {
+        super(id, logContainer, transitionDuration, stateFlags);
     }
 
     @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         RecentsView recentsView = launcher.getOverviewPanel();
         Workspace workspace = launcher.getWorkspace();
         View workspacePage = workspace.getPageAt(workspace.getCurrentPage());
@@ -65,12 +70,12 @@
         recentsView.getTaskSize(sTempRect);
         float scale = (float) sTempRect.width() / workspacePageWidth;
         float parallaxFactor = 0.5f;
-        return new float[]{scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor};
+        return new ScaleAndTranslation(scale, 0, -getDefaultSwipeHeight(launcher) * parallaxFactor);
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
-        return new float[] {1f, 0f};
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1f, 0f, 0f);
     }
 
     @Override
@@ -92,6 +97,7 @@
         DiscoveryBounce.showForOverviewIfNeeded(launcher);
     }
 
+    @Override
     public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) {
         return new PageAlphaProvider(DEACCEL_2) {
             @Override
@@ -151,4 +157,16 @@
             super.onBackPressed(launcher);
         }
     }
+
+    public static OverviewState newBackgroundState(int id) {
+        return new BackgroundAppState(id);
+    }
+
+    public static OverviewState newPeekState(int id) {
+        return new OverviewPeekState(id);
+    }
+
+    public static OverviewState newSwitchState(int id) {
+        return new QuickSwitchState(id);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
new file mode 100644
index 0000000..fa07e27
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides.states;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+
+import android.graphics.Rect;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskThumbnailView;
+import com.android.quickstep.views.TaskView;
+
+/**
+ * State to indicate we are about to launch a recent task. Note that this state is only used when
+ * quick switching from launcher; quick switching from an app uses WindowTransformSwipeHelper.
+ * @see com.android.quickstep.WindowTransformSwipeHandler.GestureEndTarget#NEW_TASK
+ */
+public class QuickSwitchState extends OverviewState {
+    private static final int STATE_FLAGS =
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+
+    public QuickSwitchState(int id) {
+        super(id, LauncherLogProto.ContainerType.APP, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    @Override
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        RecentsView recentsView = launcher.getOverviewPanel();
+        if (recentsView.getTaskViewCount() == 0) {
+            return super.getOverviewScaleAndTranslation(launcher);
+        }
+        // Compute scale and translation y such that the most recent task view fills the screen.
+        TaskThumbnailView dummyThumbnail = recentsView.getTaskViewAt(0).getThumbnail();
+        ClipAnimationHelper clipAnimationHelper = new ClipAnimationHelper(launcher);
+        clipAnimationHelper.fromTaskThumbnailView(dummyThumbnail, recentsView);
+        Rect targetRect = new Rect();
+        recentsView.getTaskSize(targetRect);
+        clipAnimationHelper.updateTargetRect(targetRect);
+        float toScale = clipAnimationHelper.getSourceRect().width()
+                / clipAnimationHelper.getTargetRect().width();
+        float toTranslationY = clipAnimationHelper.getSourceRect().centerY()
+                - clipAnimationHelper.getTargetRect().centerY();
+        return new ScaleAndTranslation(toScale, 0, toTranslationY);
+    }
+
+    @Override
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        float shiftRange = launcher.getAllAppsController().getShiftRange();
+        float shiftProgress = getVerticalProgress(launcher) - NORMAL.getVerticalProgress(launcher);
+        float translationY = shiftProgress * shiftRange;
+        return new ScaleAndTranslation(1, 0, translationY);
+    }
+
+    @Override
+    public float getVerticalProgress(Launcher launcher) {
+        return BACKGROUND_APP.getVerticalProgress(launcher);
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return NONE;
+    }
+
+    @Override
+    public void onStateTransitionEnd(Launcher launcher) {
+        TaskView tasktolaunch = launcher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
+        if (tasktolaunch != null) {
+            tasktolaunch.launchTask(false);
+        } else {
+            launcher.getStateManager().goToState(NORMAL);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
new file mode 100644
index 0000000..0757e85
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/FlingAndHoldTouchController.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
+import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
+import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_PEEK_COMPONENT;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.view.HapticFeedbackConstants;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
+import com.android.quickstep.util.MotionPauseDetector;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * Touch controller which handles swipe and hold to go to Overview
+ */
+public class FlingAndHoldTouchController extends PortraitStatesTouchController {
+
+    private static final long PEEK_ANIM_DURATION = 100;
+
+    private final MotionPauseDetector mMotionPauseDetector;
+
+    private AnimatorSet mPeekAnim;
+
+    public FlingAndHoldTouchController(Launcher l) {
+        super(l, false /* allowDragToOverview */);
+        mMotionPauseDetector = new MotionPauseDetector(l);
+    }
+
+    @Override
+    protected long getAtomicDuration() {
+        return 300;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        mMotionPauseDetector.clear();
+
+        super.onDragStart(start);
+
+        if (handlingOverviewAnim()) {
+            mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+                RecentsView recentsView = mLauncher.getOverviewPanel();
+                recentsView.setOverviewStateEnabled(isPaused);
+                if (mPeekAnim != null) {
+                    mPeekAnim.cancel();
+                }
+                LauncherState fromState = isPaused ? NORMAL : OVERVIEW_PEEK;
+                LauncherState toState = isPaused ? OVERVIEW_PEEK : NORMAL;
+                mPeekAnim = mLauncher.getStateManager().createAtomicAnimation(fromState, toState,
+                        new AnimatorSetBuilder(), ATOMIC_OVERVIEW_PEEK_COMPONENT,
+                        PEEK_ANIM_DURATION);
+                mPeekAnim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        mPeekAnim = null;
+                    }
+                });
+                mPeekAnim.start();
+                recentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+                        HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+            });
+        }
+    }
+
+    /**
+     * @return Whether we are handling the overview animation, rather than
+     * having it as part of the existing animation to the target state.
+     */
+    private boolean handlingOverviewAnim() {
+        return mStartState == NORMAL;
+    }
+
+    @Override
+    public boolean onDrag(float displacement) {
+        mMotionPauseDetector.addPosition(displacement, 0);
+        return super.onDrag(displacement);
+    }
+
+    @Override
+    public void onDragEnd(float velocity, boolean fling) {
+        if (mMotionPauseDetector.isPaused() && handlingOverviewAnim()) {
+            if (mPeekAnim != null) {
+                mPeekAnim.cancel();
+            }
+
+            AnimatorSetBuilder builder = new AnimatorSetBuilder();
+            builder.setInterpolator(AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS, OVERSHOOT_1_2);
+            AnimatorSet overviewAnim = mLauncher.getStateManager().createAtomicAnimation(
+                    NORMAL, OVERVIEW, builder, ANIM_ALL, ATOMIC_DURATION);
+            overviewAnim.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    onSwipeInteractionCompleted(OVERVIEW, Touch.SWIPE);
+                }
+            });
+            overviewAnim.start();
+        } else {
+            super.onDragEnd(velocity, fling);
+        }
+        mMotionPauseDetector.clear();
+    }
+
+    @Override
+    protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) {
+        if (handlingOverviewAnim()) {
+            // We don't want the state transition to all apps to animate overview,
+            // as that will cause a jump after our atomic animation.
+            builder.addFlag(AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
similarity index 97%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
index a069ed5..82ab34b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewToAllAppsTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/OverviewToAllAppsTouchController.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
similarity index 94%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
index eead4c8..5337c39 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/PortraitOverviewStateTouchHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitOverviewStateTouchHelper.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
-import static com.android.launcher3.uioverrides.PortraitStatesTouchController.isTouchOverHotseat;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.isTouchOverHotseat;
 
 import android.view.MotionEvent;
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
new file mode 100644
index 0000000..91a31dd
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/QuickSwitchTouchController.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.QUICK_SWITCH;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_ALL_APPS_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+
+import android.view.MotionEvent;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Handles quick switching to a recent task from the home screen.
+ */
+public class QuickSwitchTouchController extends AbstractStateChangeTouchController {
+
+    private @Nullable TaskView mTaskToLaunch;
+
+    public QuickSwitchTouchController(Launcher launcher) {
+        super(launcher, SwipeDetector.HORIZONTAL);
+    }
+
+    @Override
+    protected boolean canInterceptTouch(MotionEvent ev) {
+        if (mCurrentAnimation != null) {
+            return true;
+        }
+        if (!mLauncher.isInState(LauncherState.NORMAL)) {
+            return false;
+        }
+        if ((ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+        return isDragTowardPositive ? QUICK_SWITCH : NORMAL;
+    }
+
+    @Override
+    public void onDragStart(boolean start) {
+        super.onDragStart(start);
+        mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
+        mTaskToLaunch = mLauncher.<RecentsView>getOverviewPanel().getTaskViewAt(0);
+    }
+
+    @Override
+    protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
+        super.onSwipeInteractionCompleted(targetState, logAction);
+        mTaskToLaunch = null;
+    }
+
+    @Override
+    protected float initCurrentAnimation(int animComponents) {
+        AnimatorSetBuilder animatorSetBuilder = new AnimatorSetBuilder();
+        setupInterpolators(animatorSetBuilder);
+        long accuracy = (long) (getShiftRange() * 2);
+        mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
+                animatorSetBuilder, accuracy, this::clearState, LauncherStateManager.ANIM_ALL);
+        mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
+            updateFullscreenProgress((Float) valueAnimator.getAnimatedValue());
+        });
+        return 1 / getShiftRange();
+    }
+
+    private void setupInterpolators(AnimatorSetBuilder animatorSetBuilder) {
+        animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_FADE, DEACCEL_2);
+        animatorSetBuilder.setInterpolator(ANIM_ALL_APPS_FADE, DEACCEL_2);
+        if (FeatureFlags.SWIPE_HOME.get()) {
+            // Overview lives to the left of workspace, so translate down later than over
+            animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_SCALE, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, ACCEL_2);
+            animatorSetBuilder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+        } else {
+            animatorSetBuilder.setInterpolator(ANIM_WORKSPACE_TRANSLATE, LINEAR);
+            animatorSetBuilder.setInterpolator(ANIM_VERTICAL_PROGRESS, LINEAR);
+        }
+    }
+
+    @Override
+    protected void updateProgress(float progress) {
+        super.updateProgress(progress);
+        updateFullscreenProgress(progress);
+    }
+
+    private void updateFullscreenProgress(float progress) {
+        if (mTaskToLaunch != null) {
+            mTaskToLaunch.setFullscreenProgress(progress);
+        }
+    }
+
+    @Override
+    protected float getShiftRange() {
+        return mLauncher.getDeviceProfile().widthPx / 2f;
+    }
+
+    @Override
+    protected int getLogContainerTypeForNormalState() {
+        return LauncherLogProto.ContainerType.NAVBAR;
+    }
+
+    @Override
+    protected int getDirectionForLog() {
+        return Utilities.isRtl(mLauncher.getResources()) ? Direction.LEFT : Direction.RIGHT;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
similarity index 99%
rename from quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
rename to quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index fb1828b..a2219e8 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
 import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
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 279b83c..9763063 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -16,7 +16,6 @@
 package com.android.quickstep;
 
 import static android.view.View.TRANSLATION_Y;
-
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
 import static com.android.launcher3.LauncherState.NORMAL;
@@ -39,10 +38,6 @@
 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;
@@ -67,6 +62,10 @@
 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.
  */
@@ -305,7 +304,7 @@
         // starting to line up the side pages during swipe up)
         float prevRvScale = recentsView.getScaleX();
         float prevRvTransY = recentsView.getTranslationY();
-        float targetRvScale = endState.getOverviewScaleAndTranslationY(launcher)[0];
+        float targetRvScale = endState.getOverviewScaleAndTranslation(launcher).scale;
         SCALE_PROPERTY.set(recentsView, targetRvScale);
         recentsView.setTranslationY(0);
         ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
index 02ffdb3..7794d8d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -19,7 +19,6 @@
 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;
@@ -46,7 +45,7 @@
     private final BaseDragLayer mTarget;
     private final int[] mLocationOnScreen = new int[2];
     private final PointF mDownPos = new PointF();
-    private final int mTouchSlop;
+    private final int mTouchSlopSquared;
 
     private final boolean mStartingInActivityBounds;
 
@@ -56,7 +55,8 @@
     OverviewInputConsumer(T activity, boolean startingInActivityBounds) {
         mActivity = activity;
         mTarget = activity.getDragLayer();
-        mTouchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
+        int touchSlop = ViewConfiguration.get(mActivity).getScaledTouchSlop();
+        mTouchSlopSquared = touchSlop * touchSlop;
         mStartingInActivityBounds = startingInActivityBounds;
     }
 
@@ -88,10 +88,11 @@
                             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.
+                    float x = ev.getX() - mDownPos.x;
+                    float y = ev.getY() - mDownPos.y;
+                    double hypotSquared = x * x + y * y;
+                    if (hypotSquared >= mTouchSlopSquared) {
+                        // Start tracking only when touch slop is crossed.
                         startTouchTracking(ev, true /* updateLocationOffset */,
                                 true /* closeActiveWindows */);
                     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
index e8d4c19..15072a2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -851,16 +851,6 @@
             Interpolator interpolator, GestureEndTarget target, PointF velocityPxPerMs) {
         mGestureEndTarget = target;
 
-        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();
-        }
-
         if (mGestureEndTarget == HOME) {
             HomeAnimationFactory homeAnimFactory;
             if (mActivity != null) {
@@ -1003,10 +993,12 @@
         // Launch the task user scrolled to (mRecentsView.getNextPage()).
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             // We finish recents animation inside launchTask() when live tile is enabled.
-            mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false);
+            mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false /* animate */,
+                    true /* freezeTaskList */);
         } else {
             mRecentsAnimationWrapper.finish(true /* toRecents */, () -> {
-                mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(false);
+                mRecentsView.getTaskViewAt(mRecentsView.getNextPage()).launchTask(
+                        false /* animate */, true /* freezeTaskList */);
             });
         }
         TOUCH_INTERACTION_LOG.addLog("finishRecentsAnimation", false);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
index 9463cc9..a113604 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsTaskController.java
@@ -15,7 +15,7 @@
  */
 package com.android.quickstep.fallback;
 
-import com.android.launcher3.uioverrides.TaskViewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
 import com.android.quickstep.RecentsActivity;
 
 public class RecentsTaskController extends TaskViewTouchController<RecentsActivity> {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index a02df62..ddb94d2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -24,7 +24,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 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.uioverrides.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
+import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
 import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index eb17e3e..6ba89c9 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -250,7 +250,11 @@
     }
 
     public void launchTask(boolean animate) {
-        launchTask(animate, (result) -> {
+        launchTask(animate, false /* freezeTaskList */);
+    }
+
+    public void launchTask(boolean animate, boolean freezeTaskList) {
+        launchTask(animate, freezeTaskList, (result) -> {
             if (!result) {
                 notifyTaskLaunchFailed(TAG);
             }
@@ -259,25 +263,33 @@
 
     public void launchTask(boolean animate, Consumer<Boolean> resultCallback,
             Handler resultCallbackHandler) {
+        launchTask(animate, false /* freezeTaskList */, resultCallback, resultCallbackHandler);
+    }
+
+    public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
+            Handler resultCallbackHandler) {
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             if (isRunningTask()) {
                 getRecentsView().finishRecentsAnimation(false /* toRecents */,
                         () -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
             } else {
-                launchTaskInternal(animate, resultCallback, resultCallbackHandler);
+                launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
             }
         } else {
-            launchTaskInternal(animate, resultCallback, resultCallbackHandler);
+            launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
         }
     }
 
-    private void launchTaskInternal(boolean animate, Consumer<Boolean> resultCallback,
-            Handler resultCallbackHandler) {
+    private void launchTaskInternal(boolean animate, boolean freezeTaskList,
+            Consumer<Boolean> resultCallback, Handler resultCallbackHandler) {
         if (mTask != null) {
             final ActivityOptions opts;
             if (animate) {
                 opts = ((BaseDraggingActivity) fromContext(getContext()))
                         .getActivityLaunchOptions(this);
+                if (freezeTaskList) {
+                    ActivityOptionsCompat.setFreezeRecentTasksList(opts);
+                }
                 ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
                         opts, resultCallback, resultCallbackHandler);
             } else {
@@ -288,6 +300,9 @@
                         resultCallbackHandler.post(() -> resultCallback.accept(true));
                     }
                 }, resultCallbackHandler);
+                if (freezeTaskList) {
+                    ActivityOptionsCompat.setFreezeRecentTasksList(opts);
+                }
                 ActivityManagerWrapper.getInstance().startActivityFromRecentsAsync(mTask.key,
                         opts, (success) -> {
                             if (resultCallback != null && !success) {
diff --git a/quickstep/res/values-af/strings.xml b/quickstep/res/values-af/strings.xml
index 336eef8..88954b2 100644
--- a/quickstep/res/values-af/strings.xml
+++ b/quickstep/res/values-af/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Onlangse programme"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuut"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Program in grysskaal"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> oor vandag"</string>
 </resources>
diff --git a/quickstep/res/values-am/strings.xml b/quickstep/res/values-am/strings.xml
index 8a565e4..34fb3be 100644
--- a/quickstep/res/values-am/strings.xml
+++ b/quickstep/res/values-am/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"የቅርብ ጊዜ መተግበሪያዎች"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>፣ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ደቂቃ"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"መተግበሪያ በግራጫ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ዛሬ <xliff:g id="TIME">%1$s</xliff:g> ቀርቷል"</string>
 </resources>
diff --git a/quickstep/res/values-ar/strings.xml b/quickstep/res/values-ar/strings.xml
index d96397c..ebdcf73 100644
--- a/quickstep/res/values-ar/strings.xml
+++ b/quickstep/res/values-ar/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"التطبيقات التي تمّ استخدامها مؤخرًا"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"أقل من دقيقة"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"التطبيق بالتدرّج الرمادي"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"يتبقى اليوم <xliff:g id="TIME">%1$s</xliff:g>."</string>
 </resources>
diff --git a/quickstep/res/values-as/strings.xml b/quickstep/res/values-as/strings.xml
index 7216b4f..a33973b 100644
--- a/quickstep/res/values-as/strings.xml
+++ b/quickstep/res/values-as/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"শেহতীয়া এপসমূহ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ১ মিনিট"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"এপ্ গ্ৰে’স্কে’লত আছে"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"আজি <xliff:g id="TIME">%1$s</xliff:g> বাকী আছ"</string>
 </resources>
diff --git a/quickstep/res/values-az/strings.xml b/quickstep/res/values-az/strings.xml
index c07098e..02312f4 100644
--- a/quickstep/res/values-az/strings.xml
+++ b/quickstep/res/values-az/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son tətbiqlər"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 dəq"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Tətbiq ağ-qara rejimdədir"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Bu gün <xliff:g id="TIME">%1$s</xliff:g> qaldı"</string>
 </resources>
diff --git a/quickstep/res/values-b+sr+Latn/strings.xml b/quickstep/res/values-b+sr+Latn/strings.xml
index 6fea458..baab4a1 100644
--- a/quickstep/res/values-b+sr+Latn/strings.xml
+++ b/quickstep/res/values-b+sr+Latn/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplikacija je u sivilu"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
 </resources>
diff --git a/quickstep/res/values-be/strings.xml b/quickstep/res/values-be/strings.xml
index 8147031..b28f377 100644
--- a/quickstep/res/values-be/strings.xml
+++ b/quickstep/res/values-be/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Нядаўнія праграмы"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 хв"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Праграма ў шэрым рэжыме"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Сёння засталося <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-bg/strings.xml b/quickstep/res/values-bg/strings.xml
index 00ba582..0475c0d 100644
--- a/quickstep/res/values-bg/strings.xml
+++ b/quickstep/res/values-bg/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Скорошни приложения"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Прилож. е в сивата скала"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Оставащо време днес: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-bn/strings.xml b/quickstep/res/values-bn/strings.xml
index 9a30adf..e6764e0 100644
--- a/quickstep/res/values-bn/strings.xml
+++ b/quickstep/res/values-bn/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"সম্প্রতি ব্যবহৃত অ্যাপ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ১ মি."</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"অ্যাপে গ্রেস্কেল"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"আজকে <xliff:g id="TIME">%1$s</xliff:g> বাকি আছে"</string>
 </resources>
diff --git a/quickstep/res/values-bs/strings.xml b/quickstep/res/values-bs/strings.xml
index e940616..77b4c46 100644
--- a/quickstep/res/values-bs/strings.xml
+++ b/quickstep/res/values-bs/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Apl. je u nijansi sive"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Preostalo vrijeme: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-ca/strings.xml b/quickstep/res/values-ca/strings.xml
index d120a2c..484f445 100644
--- a/quickstep/res/values-ca/strings.xml
+++ b/quickstep/res/values-ca/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacions recents"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>; <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App en escala de grisos"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"temps restant avui: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-cs/strings.xml b/quickstep/res/values-cs/strings.xml
index 435edd9..a698d49 100644
--- a/quickstep/res/values-cs/strings.xml
+++ b/quickstep/res/values-cs/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Poslední aplikace"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuta"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplikace se zešednutím"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"dnes zbývá: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-da/strings.xml b/quickstep/res/values-da/strings.xml
index 3db6ab3..b3e8524 100644
--- a/quickstep/res/values-da/strings.xml
+++ b/quickstep/res/values-da/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Seneste apps"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Nedtonet app"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tilbage i dag"</string>
 </resources>
diff --git a/quickstep/res/values-de/strings.xml b/quickstep/res/values-de/strings.xml
index 48604f1..10e4fd7 100644
--- a/quickstep/res/values-de/strings.xml
+++ b/quickstep/res/values-de/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Zuletzt aktive Apps"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App in Graustufen"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Heute noch <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-el/strings.xml b/quickstep/res/values-el/strings.xml
index fdc7464..6ef1e94 100644
--- a/quickstep/res/values-el/strings.xml
+++ b/quickstep/res/values-el/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Πρόσφατες εφαρμογές"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 λ."</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Εφαρ. σε κλίμακα του γκρι"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> σήμερα"</string>
 </resources>
diff --git a/quickstep/res/values-en-rAU/strings.xml b/quickstep/res/values-en-rAU/strings.xml
index d625b60..d640b63 100644
--- a/quickstep/res/values-en-rAU/strings.xml
+++ b/quickstep/res/values-en-rAU/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App in grayscale"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
 </resources>
diff --git a/quickstep/res/values-en-rGB/strings.xml b/quickstep/res/values-en-rGB/strings.xml
index d625b60..d640b63 100644
--- a/quickstep/res/values-en-rGB/strings.xml
+++ b/quickstep/res/values-en-rGB/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App in grayscale"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
 </resources>
diff --git a/quickstep/res/values-en-rIN/strings.xml b/quickstep/res/values-en-rIN/strings.xml
index d625b60..d640b63 100644
--- a/quickstep/res/values-en-rIN/strings.xml
+++ b/quickstep/res/values-en-rIN/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recent apps"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minute"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App in grayscale"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> left today"</string>
 </resources>
diff --git a/quickstep/res/values-es-rUS/strings.xml b/quickstep/res/values-es-rUS/strings.xml
index a9d303a..c93e8fc 100644
--- a/quickstep/res/values-es-rUS/strings.xml
+++ b/quickstep/res/values-es-rUS/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recientes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuto"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App en escala de grises"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-es/strings.xml b/quickstep/res/values-es/strings.xml
index 291512a..3a588e5 100644
--- a/quickstep/res/values-es/strings.xml
+++ b/quickstep/res/values-es/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicaciones recientes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 minuto"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App en escala de grises"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"tiempo restante: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-et/strings.xml b/quickstep/res/values-et/strings.xml
index 96c27e7..7032765 100644
--- a/quickstep/res/values-et/strings.xml
+++ b/quickstep/res/values-et/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Hiljutised rakendused"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Rakendus halltoonides"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Tääna jäänud <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-eu/strings.xml b/quickstep/res/values-eu/strings.xml
index 15b682c..66e08b9 100644
--- a/quickstep/res/values-eu/strings.xml
+++ b/quickstep/res/values-eu/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Azken aplikazioak"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplikazioa grisen eskalan"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gelditzen dira gaur"</string>
 </resources>
diff --git a/quickstep/res/values-fa/strings.xml b/quickstep/res/values-fa/strings.xml
index 0a482f8..112d04c 100644
--- a/quickstep/res/values-fa/strings.xml
+++ b/quickstep/res/values-fa/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"برنامه‌های اخیر"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>، <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ۱ دقیقه"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"برنامه به‌صورت سیاه‌وسفید"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> باقی‌مانده برای امروز"</string>
 </resources>
diff --git a/quickstep/res/values-fi/strings.xml b/quickstep/res/values-fi/strings.xml
index 69686bb..6a0a359 100644
--- a/quickstep/res/values-fi/strings.xml
+++ b/quickstep/res/values-fi/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Viimeisimmät sovellukset"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Harmaasävyinen sovellus"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> jäljellä tänään"</string>
 </resources>
diff --git a/quickstep/res/values-fr-rCA/strings.xml b/quickstep/res/values-fr-rCA/strings.xml
index 068fd08..248a5da 100644
--- a/quickstep/res/values-fr-rCA/strings.xml
+++ b/quickstep/res/values-fr-rCA/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> : <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Application en nuances de gris"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Il reste <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
 </resources>
diff --git a/quickstep/res/values-fr/strings.xml b/quickstep/res/values-fr/strings.xml
index ea5b0dc..338d9ba 100644
--- a/quickstep/res/values-fr/strings.xml
+++ b/quickstep/res/values-fr/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Applications récentes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Appli en nuances de gris"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Encore <xliff:g id="TIME">%1$s</xliff:g> aujourd\'hui"</string>
 </resources>
diff --git a/quickstep/res/values-gl/strings.xml b/quickstep/res/values-gl/strings.xml
index a6995d0..d6ddf3c 100644
--- a/quickstep/res/values-gl/strings.xml
+++ b/quickstep/res/values-gl/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicacións recentes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App en escala de grises"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Tempo restante hoxe <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-gu/strings.xml b/quickstep/res/values-gu/strings.xml
index 72b70b0..4493e3b 100644
--- a/quickstep/res/values-gu/strings.xml
+++ b/quickstep/res/values-gu/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"તાજેતરની ઍપ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 મિનિટ"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ગ્રેસ્કેલમાં ઍપ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> આજે બાકી"</string>
 </resources>
diff --git a/quickstep/res/values-hi/strings.xml b/quickstep/res/values-hi/strings.xml
index 4d15044..3c53cce 100644
--- a/quickstep/res/values-hi/strings.xml
+++ b/quickstep/res/values-hi/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"हाल ही में इस्तेमाल किए गए एेप्लिकेशन"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 मिनट"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ग्रेस्केल में ऐप्लिकेशन"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g> और चलेगा"</string>
 </resources>
diff --git a/quickstep/res/values-hr/strings.xml b/quickstep/res/values-hr/strings.xml
index 1cec1fb..103710f 100644
--- a/quickstep/res/values-hr/strings.xml
+++ b/quickstep/res/values-hr/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Apl. u nijansama sive"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Još <xliff:g id="TIME">%1$s</xliff:g> danas"</string>
 </resources>
diff --git a/quickstep/res/values-hu/strings.xml b/quickstep/res/values-hu/strings.xml
index 112c481..22b2380 100644
--- a/quickstep/res/values-hu/strings.xml
+++ b/quickstep/res/values-hu/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Legutóbbi alkalmazások"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 perc"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Szürkeárnyalat aktív"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Ma még <xliff:g id="TIME">%1$s</xliff:g> van hátra"</string>
 </resources>
diff --git a/quickstep/res/values-hy/strings.xml b/quickstep/res/values-hy/strings.xml
index f39db5a..910265a 100644
--- a/quickstep/res/values-hy/strings.xml
+++ b/quickstep/res/values-hy/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Վերջին օգտագործած հավելվածները"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ր"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Մոխրագույն երանգներ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Այսօր մնացել է՝ <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-in/strings.xml b/quickstep/res/values-in/strings.xml
index 48892c9..a7749df 100644
--- a/quickstep/res/values-in/strings.xml
+++ b/quickstep/res/values-in/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikasi baru-baru ini"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 menit"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplikasi hitam putih"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> tersisa hari ini"</string>
 </resources>
diff --git a/quickstep/res/values-is/strings.xml b/quickstep/res/values-is/strings.xml
index c3ec96c..ba0c672 100644
--- a/quickstep/res/values-is/strings.xml
+++ b/quickstep/res/values-is/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nýleg forrit"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 mín."</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Forrit í grátónum"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> eftir í dag"</string>
 </resources>
diff --git a/quickstep/res/values-it/strings.xml b/quickstep/res/values-it/strings.xml
index d7c57ee..746443e 100644
--- a/quickstep/res/values-it/strings.xml
+++ b/quickstep/res/values-it/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"App recenti"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App in scala di grigi"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Rimanente oggi: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-iw/strings.xml b/quickstep/res/values-iw/strings.xml
index 0b745cd..96a8adc 100644
--- a/quickstep/res/values-iw/strings.xml
+++ b/quickstep/res/values-iw/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"אפליקציות אחרונות"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‏&lt; דקה"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"האפליקציה בגווני אפור"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"הזמן שנותר להיום: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-ja/strings.xml b/quickstep/res/values-ja/strings.xml
index c76c4a6..5484ae1 100644
--- a/quickstep/res/values-ja/strings.xml
+++ b/quickstep/res/values-ja/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使ったアプリ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>、<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"1 分未満"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"グレースケールのアプリ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今日はあと <xliff:g id="TIME">%1$s</xliff:g>です"</string>
 </resources>
diff --git a/quickstep/res/values-ka/strings.xml b/quickstep/res/values-ka/strings.xml
index 700522f..9218fb8 100644
--- a/quickstep/res/values-ka/strings.xml
+++ b/quickstep/res/values-ka/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ბოლოდროინდელი აპები"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 წუთი"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"აპი ნაცრისფერ ტონებშია"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"დღეს დარჩენილია <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-kk/strings.xml b/quickstep/res/values-kk/strings.xml
index 96f8956..0766150 100644
--- a/quickstep/res/values-kk/strings.xml
+++ b/quickstep/res/values-kk/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Соңғы пайдаланылған қолданбалар"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Сұр түстегі қолданба"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Бүгін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
 </resources>
diff --git a/quickstep/res/values-km/strings.xml b/quickstep/res/values-km/strings.xml
index 323efdf..8737ae8 100644
--- a/quickstep/res/values-km/strings.xml
+++ b/quickstep/res/values-km/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"កម្មវិធី​ថ្មីៗ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 នាទី"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"កម្មវិធី​ស្ថិតក្នុង​មាត្រដ្ឋាន​ពណ៌ប្រផេះ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"នៅសល់ <xliff:g id="TIME">%1$s</xliff:g> ទៀត​នៅថ្ងៃនេះ"</string>
 </resources>
diff --git a/quickstep/res/values-kn/strings.xml b/quickstep/res/values-kn/strings.xml
index b5e5738..099957c 100644
--- a/quickstep/res/values-kn/strings.xml
+++ b/quickstep/res/values-kn/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ಇತ್ತೀಚಿನ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ನಿ"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ಗ್ರೇಸ್ಕೇಲ್‌ನಲ್ಲಿ ಆ್ಯಪ್‌"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ಇಂದು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಉಳಿದಿದೆ"</string>
 </resources>
diff --git a/quickstep/res/values-ko/strings.xml b/quickstep/res/values-ko/strings.xml
index 5daa508..9543e79 100644
--- a/quickstep/res/values-ko/strings.xml
+++ b/quickstep/res/values-ko/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"최근 앱"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1분"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"앱이 그레이 스케일로 전환됨"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"오늘 <xliff:g id="TIME">%1$s</xliff:g> 남음"</string>
 </resources>
diff --git a/quickstep/res/values-ky/strings.xml b/quickstep/res/values-ky/strings.xml
index be960ec..d1d2d20 100644
--- a/quickstep/res/values-ky/strings.xml
+++ b/quickstep/res/values-ky/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Акыркы колдонмолор"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мүнөт"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Колдонмо жигерсиз"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Бүгүн <xliff:g id="TIME">%1$s</xliff:g> мүнөт калды"</string>
 </resources>
diff --git a/quickstep/res/values-lo/strings.xml b/quickstep/res/values-lo/strings.xml
index ef70ee7..aba4156 100644
--- a/quickstep/res/values-lo/strings.xml
+++ b/quickstep/res/values-lo/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ແອັບຫຼ້າສຸດ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ນາທີ"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ແອັບເປັນສີຂາວດຳ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ເຫຼືອ <xliff:g id="TIME">%1$s</xliff:g> ມື້ນີ້"</string>
 </resources>
diff --git a/quickstep/res/values-lt/strings.xml b/quickstep/res/values-lt/strings.xml
index f10866b..933b3f0 100644
--- a/quickstep/res/values-lt/strings.xml
+++ b/quickstep/res/values-lt/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Naujausios programos"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min."</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Programa su pilkumo tonu"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Šiandien liko: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-lv/strings.xml b/quickstep/res/values-lv/strings.xml
index 9ae124a..1e2ed00 100644
--- a/quickstep/res/values-lv/strings.xml
+++ b/quickstep/res/values-lv/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Pēdējās izmantotās lietotnes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 minūte"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Lietotne pelēktoņos"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Šodien atlicis: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-mk/strings.xml b/quickstep/res/values-mk/strings.xml
index 688cb93..7a6c094 100644
--- a/quickstep/res/values-mk/strings.xml
+++ b/quickstep/res/values-mk/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Неодамнешни апликации"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 минута"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Апликација во сиви тонови"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Уште <xliff:g id="TIME">%1$s</xliff:g> за денес"</string>
 </resources>
diff --git a/quickstep/res/values-ml/strings.xml b/quickstep/res/values-ml/strings.xml
index e66116c..b5eac1d 100644
--- a/quickstep/res/values-ml/strings.xml
+++ b/quickstep/res/values-ml/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"സമീപകാല ആപ്പുകൾ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 മിനിറ്റ്"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ആപ്പ് ഗ്രേസ്‌കെയിലിൽ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ഇന്ന് <xliff:g id="TIME">%1$s</xliff:g> ശേഷിക്കുന്നു"</string>
 </resources>
diff --git a/quickstep/res/values-mn/strings.xml b/quickstep/res/values-mn/strings.xml
index 5c57237..a105ef1 100644
--- a/quickstep/res/values-mn/strings.xml
+++ b/quickstep/res/values-mn/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Саяхны аппууд"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 минут"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Саарал өнгөтэй болсон апп"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Өнөөдөр <xliff:g id="TIME">%1$s</xliff:g> үлдсэн"</string>
 </resources>
diff --git a/quickstep/res/values-mr/strings.xml b/quickstep/res/values-mr/strings.xml
index f4f2904..bf725e3 100644
--- a/quickstep/res/values-mr/strings.xml
+++ b/quickstep/res/values-mr/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"अलीकडील अॅप्स"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"१मिहून कमी"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ग्रेस्केल मधील अ‍ॅप"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"आज <xliff:g id="TIME">%1$s</xliff:g>शिल्लक आहे"</string>
 </resources>
diff --git a/quickstep/res/values-ms/strings.xml b/quickstep/res/values-ms/strings.xml
index d8561eb..2e3f236 100644
--- a/quickstep/res/values-ms/strings.xml
+++ b/quickstep/res/values-ms/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apl terbaharu"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minit"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Apl dalam skala kelabu"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> lagi hari ini"</string>
 </resources>
diff --git a/quickstep/res/values-my/strings.xml b/quickstep/res/values-my/strings.xml
index f609c89..7b93125 100644
--- a/quickstep/res/values-my/strings.xml
+++ b/quickstep/res/values-my/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"လတ်တလောသုံး အက်ပ်များ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>၊ <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; ၁ မိနစ်"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"အဖြူအမည်းနှင့်ပြသော အက်ပ်"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ယနေ့ <xliff:g id="TIME">%1$s</xliff:g> ခု ကျန်သည်"</string>
 </resources>
diff --git a/quickstep/res/values-nb/strings.xml b/quickstep/res/values-nb/strings.xml
index cff48d8..74f43d2 100644
--- a/quickstep/res/values-nb/strings.xml
+++ b/quickstep/res/values-nb/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nylige apper"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minutt"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App i gråtoner"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> gjenstår i dag"</string>
 </resources>
diff --git a/quickstep/res/values-ne/strings.xml b/quickstep/res/values-ne/strings.xml
index cc450c4..6053def 100644
--- a/quickstep/res/values-ne/strings.xml
+++ b/quickstep/res/values-ne/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"हालसालैका अनुप्रयोगहरू"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; १ मिनेट"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ग्रेस्केल पारिएको एप"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"आज: <xliff:g id="TIME">%1$s</xliff:g> बाँकी"</string>
 </resources>
diff --git a/quickstep/res/values-nl/strings.xml b/quickstep/res/values-nl/strings.xml
index 4a3607d..4e3a34c 100644
--- a/quickstep/res/values-nl/strings.xml
+++ b/quickstep/res/values-nl/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Recente apps"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuut"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App in grijstinten"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Nog <xliff:g id="TIME">%1$s</xliff:g> vandaag"</string>
 </resources>
diff --git a/quickstep/res/values-or/strings.xml b/quickstep/res/values-or/strings.xml
index 4dd5920..bd88671 100644
--- a/quickstep/res/values-or/strings.xml
+++ b/quickstep/res/values-or/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ସାମ୍ପ୍ରତିକ ଆପ୍‌"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ମିନିଟ୍"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ଗ୍ରେସ୍କେଲ୍‌ରେ ଥିବା ଆପ୍"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ଆଜି <xliff:g id="TIME">%1$s</xliff:g> ବାକି ଅଛି"</string>
 </resources>
diff --git a/quickstep/res/values-pa/strings.xml b/quickstep/res/values-pa/strings.xml
index 078d8a0..5aeeae6 100644
--- a/quickstep/res/values-pa/strings.xml
+++ b/quickstep/res/values-pa/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ਹਾਲੀਆ ਐਪਾਂ"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 ਮਿੰਟ"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ਐਪ ਗ੍ਰੇਸਕੇਲ ਵਿੱਚ ਹੈ"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"ਅੱਜ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
 </resources>
diff --git a/quickstep/res/values-pl/strings.xml b/quickstep/res/values-pl/strings.xml
index aa7920c..210edcf 100644
--- a/quickstep/res/values-pl/strings.xml
+++ b/quickstep/res/values-pl/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Ostatnie aplikacje"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&gt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplikacja wyszarzona"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Na dziś zostało <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-pt-rPT/strings.xml b/quickstep/res/values-pt-rPT/strings.xml
index 22dc654..8a129d5 100644
--- a/quickstep/res/values-pt-rPT/strings.xml
+++ b/quickstep/res/values-pt-rPT/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicações recentes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minuto"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplic. na Escala de cinz."</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Resta(m) <xliff:g id="TIME">%1$s</xliff:g> hoje."</string>
 </resources>
diff --git a/quickstep/res/values-pt/strings.xml b/quickstep/res/values-pt/strings.xml
index b838c96..e5380d5 100644
--- a/quickstep/res/values-pt/strings.xml
+++ b/quickstep/res/values-pt/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Apps recentes"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App em escala de cinza"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> restante(s) hoje"</string>
 </resources>
diff --git a/quickstep/res/values-ro/strings.xml b/quickstep/res/values-ro/strings.xml
index a33524e..54452a0 100644
--- a/quickstep/res/values-ro/strings.xml
+++ b/quickstep/res/values-ro/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplicații recente"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minut"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplicație în tonuri de gri"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Au mai rămas <xliff:g id="TIME">%1$s</xliff:g> astăzi"</string>
 </resources>
diff --git a/quickstep/res/values-ru/strings.xml b/quickstep/res/values-ru/strings.xml
index 2df583c..8b2016a 100644
--- a/quickstep/res/values-ru/strings.xml
+++ b/quickstep/res/values-ru/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавние приложения"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>: <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин."</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"В режиме оттенков серого"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Осталось сегодня: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-si/strings.xml b/quickstep/res/values-si/strings.xml
index 010a56e..2163390 100644
--- a/quickstep/res/values-si/strings.xml
+++ b/quickstep/res/values-si/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"මෑත යෙදුම්"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 විනාඩියක්"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"අලු පැහැ යෙදුම"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"අද <xliff:g id="TIME">%1$s</xliff:g>ක් ඉතුරුයි"</string>
 </resources>
diff --git a/quickstep/res/values-sk/strings.xml b/quickstep/res/values-sk/strings.xml
index c73a597..12983db 100644
--- a/quickstep/res/values-sk/strings.xml
+++ b/quickstep/res/values-sk/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedávne aplikácie"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"Menej ako 1 minúta"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Aplikácia je odfarbená"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Dnes ešte zostáva: <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-sl/strings.xml b/quickstep/res/values-sl/strings.xml
index a7f0704..a940f2b 100644
--- a/quickstep/res/values-sl/strings.xml
+++ b/quickstep/res/values-sl/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Nedavne aplikacije"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Črnobela aplikacija"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Danes je ostalo še <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-sq/strings.xml b/quickstep/res/values-sq/strings.xml
index b70f142..e41bcb5 100644
--- a/quickstep/res/values-sq/strings.xml
+++ b/quickstep/res/values-sq/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Aplikacionet e fundit"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 minutë"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Apl. në shkallën e grisë"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> të mbetura sot"</string>
 </resources>
diff --git a/quickstep/res/values-sr/strings.xml b/quickstep/res/values-sr/strings.xml
index 2cf43f5..8f26c66 100644
--- a/quickstep/res/values-sr/strings.xml
+++ b/quickstep/res/values-sr/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Недавне апликације"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 мин"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Апликација је у сивилу"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Још <xliff:g id="TIME">%1$s</xliff:g> данас"</string>
 </resources>
diff --git a/quickstep/res/values-sv/strings.xml b/quickstep/res/values-sv/strings.xml
index bec4893..70740e5 100644
--- a/quickstep/res/values-sv/strings.xml
+++ b/quickstep/res/values-sv/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Senaste apparna"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App visas i gråskala"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> kvar i dag"</string>
 </resources>
diff --git a/quickstep/res/values-sw/strings.xml b/quickstep/res/values-sw/strings.xml
index 53d095d..c646b6a 100644
--- a/quickstep/res/values-sw/strings.xml
+++ b/quickstep/res/values-sw/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Programu za hivi karibuni"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; dak 1"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Programu katika kijivu"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Umebakisha <xliff:g id="TIME">%1$s</xliff:g> leo"</string>
 </resources>
diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml
index ecc8f6c..19bfaa9 100644
--- a/quickstep/res/values-ta/strings.xml
+++ b/quickstep/res/values-ta/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"சமீபத்திய ஆப்ஸ்"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 நி"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"கிரேஸ்கேலில் உள்ள ஆப்ஸ்"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"இன்று <xliff:g id="TIME">%1$s</xliff:g> மீதமுள்ளது"</string>
 </resources>
diff --git a/quickstep/res/values-te/strings.xml b/quickstep/res/values-te/strings.xml
index b6eeb40..071755a 100644
--- a/quickstep/res/values-te/strings.xml
+++ b/quickstep/res/values-te/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"ఇటీవలి యాప్‌లు"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 నిమిషం"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"యాప్‌ గ్రేస్కేల్లో ఉంది"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"నేటికి <xliff:g id="TIME">%1$s</xliff:g> మిగిలి ఉంది"</string>
 </resources>
diff --git a/quickstep/res/values-th/strings.xml b/quickstep/res/values-th/strings.xml
index c2e173a..c0e78ce 100644
--- a/quickstep/res/values-th/strings.xml
+++ b/quickstep/res/values-th/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"แอปล่าสุด"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt;1 นาที"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"แอปที่เป็นโทนสีเทา"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"วันนี้เหลืออีก <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-tl/strings.xml b/quickstep/res/values-tl/strings.xml
index 5588d03..76a0b25 100644
--- a/quickstep/res/values-tl/strings.xml
+++ b/quickstep/res/values-tl/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Mga kamakailang app"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 min"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"App na grayscale"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> na lang ngayon"</string>
 </resources>
diff --git a/quickstep/res/values-tr/strings.xml b/quickstep/res/values-tr/strings.xml
index 40e0d89..8b59c7b 100644
--- a/quickstep/res/values-tr/strings.xml
+++ b/quickstep/res/values-tr/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Son uygulamalar"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 dk."</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Uygulama gri tonlamada"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Bugün <xliff:g id="TIME">%1$s</xliff:g> kaldı"</string>
 </resources>
diff --git a/quickstep/res/values-uk/strings.xml b/quickstep/res/values-uk/strings.xml
index 104c82c..39c3848 100644
--- a/quickstep/res/values-uk/strings.xml
+++ b/quickstep/res/values-uk/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Нещодавні додатки"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 хв"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Додаток у відтінку сірого"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Сьогодні залишилося <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-ur/strings.xml b/quickstep/res/values-ur/strings.xml
index 481bda0..4fd9e69 100644
--- a/quickstep/res/values-ur/strings.xml
+++ b/quickstep/res/values-ur/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"حالیہ ایپس"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>،<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"‏&lt; 1 منٹ"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"ایپ خاکستری کیا گیا"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"آج <xliff:g id="TIME">%1$s</xliff:g> بچا ہے"</string>
 </resources>
diff --git a/quickstep/res/values-uz/strings.xml b/quickstep/res/values-uz/strings.xml
index 7e9f955..466d79e 100644
--- a/quickstep/res/values-uz/strings.xml
+++ b/quickstep/res/values-uz/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Yaqinda ishlatilgan ilovalar"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 daqiqa"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Ilova kulrang rejimida"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Bugun <xliff:g id="TIME">%1$s</xliff:g> qoldi"</string>
 </resources>
diff --git a/quickstep/res/values-vi/strings.xml b/quickstep/res/values-vi/strings.xml
index 3d42063..842b22b 100644
--- a/quickstep/res/values-vi/strings.xml
+++ b/quickstep/res/values-vi/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Ứng dụng gần đây"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 phút"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Ứng dụng có thang màu xám"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"Hôm nay còn <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rCN/strings.xml b/quickstep/res/values-zh-rCN/strings.xml
index 4a13d14..951489f 100644
--- a/quickstep/res/values-zh-rCN/strings.xml
+++ b/quickstep/res/values-zh-rCN/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近用过的应用"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>(<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"不到 1 分钟"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"灰度模式下的应用"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今天还可使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rHK/strings.xml b/quickstep/res/values-zh-rHK/strings.xml
index 02d9b8e..361623d 100644
--- a/quickstep/res/values-zh-rHK/strings.xml
+++ b/quickstep/res/values-zh-rHK/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>,<xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"少於 1 分鐘"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"切換至灰階螢幕的應用程式"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今天剩餘時間:<xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-zh-rTW/strings.xml b/quickstep/res/values-zh-rTW/strings.xml
index 20181fd..6938d3e 100644
--- a/quickstep/res/values-zh-rTW/strings.xml
+++ b/quickstep/res/values-zh-rTW/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"最近使用的應用程式"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g> (<xliff:g id="REMAINING_TIME">%2$s</xliff:g>)"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 分鐘"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"切換為灰階模式的應用程式"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"今天還能使用 <xliff:g id="TIME">%1$s</xliff:g>"</string>
 </resources>
diff --git a/quickstep/res/values-zu/strings.xml b/quickstep/res/values-zu/strings.xml
index abbbc95..98f7b02 100644
--- a/quickstep/res/values-zu/strings.xml
+++ b/quickstep/res/values-zu/strings.xml
@@ -30,6 +30,5 @@
     <string name="accessibility_recent_apps" msgid="4058661986695117371">"Izinhlelo zokusebenza zakamuva"</string>
     <string name="task_contents_description_with_remaining_time" msgid="4479688746574672685">"<xliff:g id="TASK_DESCRIPTION">%1$s</xliff:g>, <xliff:g id="REMAINING_TIME">%2$s</xliff:g>"</string>
     <string name="shorter_duration_less_than_one_minute" msgid="4722015666335015336">"&lt; 1 iminithi"</string>
-    <string name="app_in_grayscale" msgid="1108706002158384887">"Uhlelo lokusebenza nge-grayscale"</string>
     <string name="time_left_for_app" msgid="3111996412933644358">"<xliff:g id="TIME">%1$s</xliff:g> esele namhlanje"</string>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 9c97c8c..97f2de7 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -24,7 +24,7 @@
     <dimen name="task_corner_radius_small">2dp</dimen>
     <dimen name="recents_page_spacing">10dp</dimen>
     <dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
-    <dimen name="quickscrub_adjacent_visible_width">20dp</dimen>
+    <dimen name="overview_peek_distance">32dp</dimen>
 
     <!-- The speed in dp/s at which the user needs to be scrolling in recents such that we start
              loading full resolution screenshots. -->
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index e74d84d..f0204b9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -19,6 +19,9 @@
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_Y;
+import static com.android.launcher3.anim.AnimatorSetBuilder.FLAG_DONT_ANIMATE_OVERVIEW;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
@@ -28,6 +31,7 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.LauncherStateManager.StateHandler;
 import com.android.launcher3.anim.AnimatorSetBuilder;
@@ -53,19 +57,30 @@
 
     @Override
     public void setState(@NonNull LauncherState state) {
-        float[] scaleTranslationY = state.getOverviewScaleAndTranslationY(mLauncher);
-        SCALE_PROPERTY.set(mRecentsView, scaleTranslationY[0]);
-        mRecentsView.setTranslationY(scaleTranslationY[1]);
+        ScaleAndTranslation scaleAndTranslation = state
+                .getOverviewScaleAndTranslation(mLauncher);
+        SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
+        float translationX = scaleAndTranslation.translationX;
+        if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            translationX = -translationX;
+        }
+        mRecentsView.setTranslationX(translationX);
+        mRecentsView.setTranslationY(scaleAndTranslation.translationY);
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
     }
 
     @Override
     public final void setStateWithAnimation(@NonNull final LauncherState toState,
             @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
-        if (!config.playAtomicComponent()) {
+        boolean playAtomicOverviewComponent = config.playAtomicOverviewScaleComponent()
+                || config.playAtomicOverviewPeekComponent();
+        if (!playAtomicOverviewComponent) {
             // The entire recents animation is played atomically.
             return;
         }
+        if (builder.hasFlag(FLAG_DONT_ANIMATE_OVERVIEW)) {
+            return;
+        }
         setStateWithAnimationInternal(toState, builder, config);
     }
 
@@ -79,29 +94,25 @@
     void setStateWithAnimationInternal(@NonNull final LauncherState toState,
             @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
         PropertySetter setter = config.getPropertySetter(builder);
-        float[] scaleTranslationY = toState.getOverviewScaleAndTranslationY(mLauncher);
-        Interpolator scaleAndTransYInterpolator = getScaleAndTransYInterpolator(toState, builder);
-        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationY[0],
-                scaleAndTransYInterpolator);
-        setter.setFloat(mRecentsView, View.TRANSLATION_Y, scaleTranslationY[1],
-                scaleAndTransYInterpolator);
+        ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
+        Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
+        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
+        Interpolator translateXInterpolator = builder.getInterpolator(
+                ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
+        Interpolator translateYInterpolator = builder.getInterpolator(
+                ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
+        float translationX = scaleAndTranslation.translationX;
+        if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            translationX = -translationX;
+        }
+        setter.setFloat(mRecentsView, View.TRANSLATION_X, translationX, translateXInterpolator);
+        setter.setFloat(mRecentsView, View.TRANSLATION_Y, scaleAndTranslation.translationY,
+                translateYInterpolator);
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
     }
 
     /**
-     * Get the interpolator to use for the scale and translation Y animation for the view.
-     *
-     * @param toState state to animate to
-     * @param builder animator set builder
-     * @return interpolator for scale and trans Y recents view animation
-     */
-    Interpolator getScaleAndTransYInterpolator(@NonNull final LauncherState toState,
-            @NonNull AnimatorSetBuilder builder) {
-        return builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
-    }
-
-    /**
      * Get property for content alpha for the recents view.
      *
      * @return the float property for the view's content alpha
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
similarity index 84%
rename from quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
rename to quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
index 5ae562e..ab24f5f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
@@ -60,10 +60,10 @@
     }
 
     @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
-        float[] scaleAndTranslation = LauncherState.OVERVIEW.getWorkspaceScaleAndTranslation(
-                launcher);
-        scaleAndTranslation[0] = 1;
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        ScaleAndTranslation scaleAndTranslation = LauncherState.OVERVIEW
+                .getWorkspaceScaleAndTranslation(launcher);
+        scaleAndTranslation.scale = 1;
         return scaleAndTranslation;
     }
 
@@ -78,9 +78,9 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
         float slightParallax = -launcher.getDeviceProfile().allAppsCellHeightPx * 0.3f;
-        return new float[] {0.9f, slightParallax};
+        return new ScaleAndTranslation(0.9f, 0f, slightParallax);
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
similarity index 96%
rename from quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
index 086cbdb..0605953 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/LandscapeEdgeSwipeController.java
@@ -1,4 +1,4 @@
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -41,7 +41,7 @@
 
     @Override
     protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
-        boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() != isDragTowardPositive;
+        boolean draggingFromNav = mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
         return draggingFromNav ? OVERVIEW : NORMAL;
     }
 
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
similarity index 96%
rename from quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index d20ffbb..ce50b68 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static com.android.launcher3.AbstractFloatingView.TYPE_ACCESSIBLE;
 import static com.android.launcher3.LauncherState.ALL_APPS;
@@ -43,6 +43,8 @@
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.touch.AbstractStateChangeTouchController;
 import com.android.launcher3.touch.SwipeDetector;
+import com.android.launcher3.uioverrides.states.OverviewState;
+import com.android.launcher3.uioverrides.touchcontrollers.PortraitOverviewStateTouchHelper;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.quickstep.RecentsModel;
@@ -195,6 +197,7 @@
 
         final AnimatorSetBuilder builder = totalShift == 0 ? new AnimatorSetBuilder()
                 : getAnimatorSetBuilderForStates(mFromState, mToState);
+        updateAnimatorBuilderOnReinit(builder);
 
         cancelPendingAnim();
 
@@ -228,6 +231,12 @@
         return 1 / totalShift;
     }
 
+    /**
+     * Give subclasses the chance to update the animation when we re-initialize towards a new state.
+     */
+    protected void updateAnimatorBuilderOnReinit(AnimatorSetBuilder builder) {
+    }
+
     private void cancelPendingAnim() {
         if (mPendingAnimation != null) {
             mPendingAnimation.finish(false, Touch.SWIPE);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
similarity index 98%
rename from quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java
rename to quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
index 8f33e40..12e6f12 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/StatusBarTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/StatusBarTouchController.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.touchcontrollers;
 
 import static android.view.MotionEvent.ACTION_DOWN;
 import static android.view.MotionEvent.ACTION_MOVE;
diff --git a/quickstep/src/com/android/quickstep/NavBarModeOverlayResourceObserver.java b/quickstep/src/com/android/quickstep/NavBarModeOverlayResourceObserver.java
new file mode 100644
index 0000000..3136632
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/NavBarModeOverlayResourceObserver.java
@@ -0,0 +1,89 @@
+/*
+ * 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.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.util.Log;
+
+import com.android.systemui.shared.system.QuickStepContract;
+
+/**
+ * Observer for the resource config that specifies the navigation bar mode.
+ */
+public class NavBarModeOverlayResourceObserver extends BroadcastReceiver {
+
+    private static final String TAG = "NavBarModeOverlayResourceObserver";
+
+    private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+    private static final String NAV_BAR_INTERACTION_MODE_RES_NAME =
+            "config_navBarInteractionMode";
+
+    private final Context mContext;
+    private final OnChangeListener mOnChangeListener;
+
+    public NavBarModeOverlayResourceObserver(Context context, OnChangeListener listener) {
+        mContext = context;
+        mOnChangeListener = listener;
+    }
+
+    public void register() {
+        IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(this, filter);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        mOnChangeListener.onNavBarModeChanged(getSystemIntegerRes(context,
+                NAV_BAR_INTERACTION_MODE_RES_NAME));
+    }
+
+    public interface OnChangeListener {
+        void onNavBarModeChanged(int mode);
+    }
+
+    public static boolean isSwipeUpModeEnabled(Context context) {
+        return QuickStepContract.isSwipeUpMode(getSystemIntegerRes(context,
+                NAV_BAR_INTERACTION_MODE_RES_NAME));
+    }
+
+    public static boolean isEdgeToEdgeModeEnabled(Context context) {
+        return QuickStepContract.isGesturalMode(getSystemIntegerRes(context,
+                NAV_BAR_INTERACTION_MODE_RES_NAME));
+    }
+
+    public static boolean isLegacyModeEnabled(Context context) {
+        return QuickStepContract.isLegacyMode(getSystemIntegerRes(context,
+                NAV_BAR_INTERACTION_MODE_RES_NAME));
+    }
+
+    private static int getSystemIntegerRes(Context context, String resName) {
+        Resources res = context.getResources();
+        int resId = res.getIdentifier(resName, "integer", "android");
+
+        if (resId != 0) {
+            return res.getInteger(resId);
+        } else {
+            Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
+            return -1;
+        }
+    }
+}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
index a0ab301..903701d 100644
--- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java
+++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java
@@ -15,7 +15,6 @@
  */
 package com.android.quickstep;
 
-import static com.android.quickstep.SwipeUpSetting.newSwipeUpSettingsObserver;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_QUICK_SCRUB;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
@@ -28,10 +27,11 @@
 
 import com.android.launcher3.Utilities;
 import com.android.launcher3.allapps.DiscoveryBounce;
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.util.MainThreadInitializedObject;
-import com.android.launcher3.util.SecureSettingsObserver;
 import com.android.launcher3.util.UiThreadHelper;
 import com.android.systemui.shared.recents.ISystemUiProxy;
+import com.android.systemui.shared.system.QuickStepContract;
 
 import androidx.annotation.WorkerThread;
 
@@ -58,7 +58,8 @@
     private static final int MSG_SET_BACK_BUTTON_ALPHA = 201;
     private static final int MSG_SET_SWIPE_UP_ENABLED = 202;
 
-    private final SecureSettingsObserver mSwipeUpSettingObserver;
+    // TODO: Discriminate between swipe up and edge to edge
+    private final NavBarModeOverlayResourceObserver mSwipeUpSettingObserver;
 
     private final Context mContext;
     private final Handler mUiHandler;
@@ -66,7 +67,7 @@
 
     // These are updated on the background thread
     private ISystemUiProxy mISystemUiProxy;
-    private boolean mSwipeUpEnabled = true;
+    private boolean mSwipeUpEnabled;
     private float mBackButtonAlpha = 1;
 
     private Runnable mOnSwipeUpSettingChangedListener;
@@ -80,15 +81,15 @@
         mUiHandler = new Handler(this::handleUiMessage);
         mBgHandler = new Handler(UiThreadHelper.getBackgroundLooper(), this::handleBgMessage);
 
-        if (SwipeUpSetting.isSwipeUpSettingAvailable()) {
-            mSwipeUpSettingObserver =
-                    newSwipeUpSettingsObserver(context, this::notifySwipeUpSettingChanged);
+        mSwipeUpEnabled = NavBarModeOverlayResourceObserver.isSwipeUpModeEnabled(mContext)
+                || NavBarModeOverlayResourceObserver.isEdgeToEdgeModeEnabled(mContext);
+        if (SwipeUpSetting.isSystemNavigationSettingAvailable()) {
+            mSwipeUpSettingObserver = new NavBarModeOverlayResourceObserver(context,
+                    this::notifySwipeUpSettingChanged);
             mSwipeUpSettingObserver.register();
-            mSwipeUpEnabled = mSwipeUpSettingObserver.getValue();
             resetHomeBounceSeenOnQuickstepEnabledFirstTime();
         } else {
             mSwipeUpSettingObserver = null;
-            mSwipeUpEnabled = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
         }
     }
 
@@ -175,7 +176,13 @@
         }
     }
 
-    private void notifySwipeUpSettingChanged(boolean swipeUpEnabled) {
+    private void notifySwipeUpSettingChanged(int mode) {
+        boolean swipeUpEnabled = !QuickStepContract.isLegacyMode(mode);
+        boolean gesturalEnabled = QuickStepContract.isGesturalMode(mode);
+
+        FeatureFlags.SWIPE_HOME.updateStorage(mContext, gesturalEnabled);
+        FeatureFlags.ENABLE_ASSISTANT_GESTURE.updateStorage(mContext, gesturalEnabled);
+
         mUiHandler.removeMessages(MSG_SET_SWIPE_UP_ENABLED);
         mUiHandler.obtainMessage(MSG_SET_SWIPE_UP_ENABLED, swipeUpEnabled ? 1 : 0, 0).
                 sendToTarget();
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index e15a3f1..06a36c9 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -44,7 +44,6 @@
     private final KeyguardManagerCompat mKeyguardManager;
     private final MainThreadExecutor mMainThreadExecutor;
     private final BackgroundExecutor mBgThreadExecutor;
-    private final TaskListStabilizer mStabilizer = new TaskListStabilizer();
 
     // The list change id, increments as the task list changes in the system
     private int mChangeId;
@@ -74,14 +73,6 @@
         });
     }
 
-    public void startStabilizationSession() {
-        mStabilizer.startStabilizationSession();
-    }
-
-    public void endStabilizationSession() {
-        mStabilizer.endStabilizationSession();
-    }
-
     /**
      * Asynchronously fetches the list of recent tasks, reusing cached list if available.
      *
@@ -93,7 +84,7 @@
         final int requestLoadId = mChangeId;
         Runnable resultCallback = callback == null
                 ? () -> { }
-                : () -> callback.accept(mStabilizer.reorder(mTasks));
+                : () -> callback.accept(mTasks);
 
         if (mLastLoadedId == mChangeId && (!mLastLoadHadKeysOnly || loadKeysOnly)) {
             // The list is up to date, callback with the same list
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 56bc857..a65bc33 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -90,14 +90,6 @@
         return mThumbnailCache;
     }
 
-    public void startStabilizationSession() {
-        mTaskList.startStabilizationSession();
-    }
-
-    public void endStabilizationSession() {
-        mTaskList.endStabilizationSession();
-    }
-
     /**
      * Fetches the list of recent tasks.
      *
diff --git a/quickstep/src/com/android/quickstep/SwipeUpSetting.java b/quickstep/src/com/android/quickstep/SwipeUpSetting.java
index 381ab9f..7f830f9 100644
--- a/quickstep/src/com/android/quickstep/SwipeUpSetting.java
+++ b/quickstep/src/com/android/quickstep/SwipeUpSetting.java
@@ -16,23 +16,18 @@
 
 package com.android.quickstep;
 
-import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
-
-import android.content.Context;
 import android.content.res.Resources;
 import android.util.Log;
 
-import com.android.launcher3.util.SecureSettingsObserver;
-import com.android.launcher3.util.SecureSettingsObserver.OnChangeListener;
-
 public final class SwipeUpSetting {
     private static final String TAG = "SwipeUpSetting";
 
     private static final String SWIPE_UP_SETTING_AVAILABLE_RES_NAME =
             "config_swipe_up_gesture_setting_available";
 
-    private static final String SWIPE_UP_ENABLED_DEFAULT_RES_NAME =
-            "config_swipe_up_gesture_default";
+    public static boolean isSystemNavigationSettingAvailable() {
+        return getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME);
+    }
 
     private static boolean getSystemBooleanRes(String resName) {
         Resources res = Resources.getSystem();
@@ -45,18 +40,4 @@
             return false;
         }
     }
-
-    public static boolean isSwipeUpSettingAvailable() {
-        return getSystemBooleanRes(SWIPE_UP_SETTING_AVAILABLE_RES_NAME);
-    }
-
-    public static boolean isSwipeUpEnabledDefaultValue() {
-        return getSystemBooleanRes(SWIPE_UP_ENABLED_DEFAULT_RES_NAME);
-    }
-
-    public static SecureSettingsObserver newSwipeUpSettingsObserver(Context context,
-            OnChangeListener listener) {
-        return new SecureSettingsObserver(context.getContentResolver(), listener,
-                SWIPE_UP_SETTING_NAME, isSwipeUpEnabledDefaultValue() ? 1 : 0);
-    }
 }
diff --git a/quickstep/src/com/android/quickstep/TaskListStabilizer.java b/quickstep/src/com/android/quickstep/TaskListStabilizer.java
deleted file mode 100644
index 4c63f81..0000000
--- a/quickstep/src/com/android/quickstep/TaskListStabilizer.java
+++ /dev/null
@@ -1,145 +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;
-
-import android.app.ActivityManager.RecentTaskInfo;
-import android.content.ComponentName;
-import android.os.Process;
-import android.os.SystemClock;
-
-import com.android.launcher3.util.IntArray;
-import com.android.systemui.shared.recents.model.Task;
-import com.android.systemui.shared.recents.model.Task.TaskKey;
-import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.TaskStackChangeListener;
-
-import java.util.ArrayList;
-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 final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
-
-        @Override
-        public void onTaskCreated(int taskId, ComponentName componentName) {
-            endStabilizationSession();
-        }
-
-        @Override
-        public void onTaskRemoved(int taskId) {
-            endStabilizationSession();
-        }
-    };
-
-    // 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<>();
-
-    private boolean mInStabilizationSession;
-    private long mSessionStartTime;
-
-    public TaskListStabilizer() {
-        // 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);
-    }
-
-    public synchronized void startStabilizationSession() {
-        if (!mInStabilizationSession) {
-            mStabilizedOrder.clear();
-            mStabilizedOrder.addAll(mSystemOrder);
-        }
-        mInStabilizationSession = true;
-        mSessionStartTime = SystemClock.uptimeMillis();
-    }
-
-    public synchronized void endStabilizationSession() {
-        mInStabilizationSession = false;
-    }
-
-    public synchronized ArrayList<Task> reorder(ArrayList<Task> tasks) {
-        mSystemOrder.clear();
-        for (Task task : tasks) {
-            mSystemOrder.add(task.key.id);
-        }
-
-        if ((SystemClock.uptimeMillis() - mSessionStartTime) > TASK_CACHE_TIMEOUT_MS) {
-            endStabilizationSession();
-        }
-
-        if (!mInStabilizationSession) {
-            return tasks;
-        }
-
-        // Ensure that we have enough wrappers
-        int taskCount = tasks.size();
-        for (int i = taskCount - mTaskWrappers.size(); i > 0; i--) {
-            mTaskWrappers.add(new TaskWrapper());
-        }
-
-        List<TaskWrapper> listToSort = mTaskWrappers.size() == taskCount
-                ? mTaskWrappers : mTaskWrappers.subList(0, taskCount);
-        int missingTaskIndex = -taskCount;
-
-        for (int i = 0; i < taskCount; i++){
-            TaskWrapper wrapper = listToSort.get(i);
-            wrapper.task = tasks.get(i);
-            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
-            if (wrapper.index < 0) {
-                wrapper.index = missingTaskIndex;
-                missingTaskIndex++;
-            }
-        }
-        Collections.sort(listToSort);
-
-        ArrayList<Task> result = new ArrayList<>(taskCount);
-        for (int i = 0; i < taskCount; i++) {
-            result.add(listToSort.get(i).task);
-        }
-        return result;
-    }
-
-    private static class TaskWrapper implements Comparable<TaskWrapper> {
-        Task task;
-        int index;
-
-        @Override
-        public int compareTo(TaskWrapper other) {
-            return Integer.compare(index, other.index);
-        }
-    }
-}
diff --git a/quickstep/src/com/android/quickstep/TestInformationProvider.java b/quickstep/src/com/android/quickstep/TestInformationProvider.java
index e57d3ec..b37ddda 100644
--- a/quickstep/src/com/android/quickstep/TestInformationProvider.java
+++ b/quickstep/src/com/android/quickstep/TestInformationProvider.java
@@ -30,7 +30,7 @@
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.TestProtocol;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.uioverrides.OverviewState;
+import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.quickstep.util.LayoutUtils;
 
 public class TestInformationProvider extends ContentProvider {
diff --git a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
index 8a117c8..96620bd 100644
--- a/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
+++ b/quickstep/src/com/android/quickstep/util/MotionPauseDetector.java
@@ -71,9 +71,6 @@
      */
     public void setOnMotionPauseListener(OnMotionPauseListener listener) {
         mOnMotionPauseListener = listener;
-        if (mOnMotionPauseListener != null) {
-            mOnMotionPauseListener.onMotionPauseChanged(mIsPaused);
-        }
     }
 
     /**
diff --git a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
index 6854aa8..c77726e 100644
--- a/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
+++ b/quickstep/tests/src/com/android/quickstep/AbstractQuickStepTest.java
@@ -27,5 +27,5 @@
 public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
     @Rule
     public TestRule mQuickstepOnOffExecutor =
-            new QuickStepOnOffRule(mMainThreadExecutor, mLauncher);
+            new NavigationModeSwitchRule(mLauncher);
 }
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 88b50d9..f436831 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -17,26 +17,31 @@
 
 import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
 
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+
 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
 import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
 import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
 import static com.android.launcher3.util.rule.ShellCommandRule.disableHeadsUpNotification;
 import static com.android.launcher3.util.rule.ShellCommandRule.getLauncherCommand;
-import static com.android.quickstep.QuickStepOnOffRule.Mode.OFF;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
 
 import static org.junit.Assert.assertTrue;
 
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 
-import com.android.launcher3.MainThreadExecutor;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
 import com.android.launcher3.tapl.LauncherInstrumentation;
 import com.android.launcher3.testcomponent.TestCommandReceiver;
-import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -44,12 +49,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.model.Statement;
 
-import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
-import androidx.test.uiautomator.By;
-import androidx.test.uiautomator.UiDevice;
-import androidx.test.uiautomator.Until;
-
 @LargeTest
 @RunWith(AndroidJUnit4.class)
 /**
@@ -72,7 +71,7 @@
         mDevice = UiDevice.getInstance(instrumentation);
         mLauncher = new LauncherInstrumentation(instrumentation);
 
-        mQuickstepOnOffExecutor = new QuickStepOnOffRule(new MainThreadExecutor(), mLauncher);
+        mQuickstepOnOffExecutor = new NavigationModeSwitchRule(mLauncher);
         mOtherLauncherActivity = context.getPackageManager().queryIntentActivities(
                 getHomeIntentInPackage(context),
                 MATCH_DISABLED_COMPONENTS).get(0).activityInfo;
@@ -94,7 +93,7 @@
         };
     }
 
-    @QuickstepOnOff(mode = OFF)
+    @NavigationModeSwitch(mode = THREE_BUTTON)
     @Test
     public void goToOverviewFromHome() {
         mDevice.pressHome();
@@ -104,7 +103,7 @@
         mLauncher.getBackground().switchToOverview();
     }
 
-    @QuickstepOnOff(mode = OFF)
+    @NavigationModeSwitch(mode = THREE_BUTTON)
     @Test
     public void goToOverviewFromApp() {
         startAppFast("com.android.settings");
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
new file mode 100644
index 0000000..8633b21
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -0,0 +1,153 @@
+/*
+ * 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 androidx.test.InstrumentationRegistry.getInstrumentation;
+
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.ALL;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.THREE_BUTTON;
+import static com.android.quickstep.NavigationModeSwitchRule.Mode.TWO_BUTTON;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_2BUTTON_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_3BUTTON_OVERLAY;
+import static com.android.systemui.shared.system.QuickStepContract.NAV_BAR_MODE_GESTURAL_OVERLAY;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.test.uiautomator.UiDevice;
+
+import com.android.launcher3.tapl.LauncherInstrumentation;
+import com.android.launcher3.tapl.TestHelpers;
+import com.android.systemui.shared.system.QuickStepContract;
+
+import org.junit.Assert;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Test rule that allows executing a test with Quickstep on and then Quickstep off.
+ * The test should be annotated with @QuickstepOnOff.
+ */
+public class NavigationModeSwitchRule implements TestRule {
+
+    static final String TAG = "QuickStepOnOffRule";
+
+    public enum Mode {
+        THREE_BUTTON, TWO_BUTTON, ZERO_BUTTON, ALL
+    }
+
+    // Annotation for tests that need to be run with quickstep enabled and disabled.
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public @interface NavigationModeSwitch {
+        Mode mode() default ALL;
+    }
+
+    private final LauncherInstrumentation mLauncher;
+
+    public NavigationModeSwitchRule(LauncherInstrumentation launcher) {
+        mLauncher = launcher;
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        if (TestHelpers.isInLauncherProcess() &&
+                description.getAnnotation(NavigationModeSwitch.class) != null) {
+            Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
+            return new Statement() {
+                @Override
+                public void evaluate() throws Throwable {
+                    final Context context = getInstrumentation().getContext();
+                    final String prevOverlayPkg = QuickStepContract.isGesturalMode(context)
+                            ? NAV_BAR_MODE_GESTURAL_OVERLAY
+                            : QuickStepContract.isSwipeUpMode(context)
+                                    ? NAV_BAR_MODE_2BUTTON_OVERLAY
+                                    : NAV_BAR_MODE_3BUTTON_OVERLAY;
+                    final LauncherInstrumentation.NavigationModel originalMode =
+                            mLauncher.getNavigationModel();
+                    try {
+//                        if (mode == ZERO_BUTTON || mode == ALL) {
+//                            evaluateWithZeroButtons();
+//                        }
+                        if (mode == TWO_BUTTON || mode == ALL) {
+                            evaluateWithTwoButtons();
+                        }
+                        if (mode == THREE_BUTTON || mode == ALL) {
+                            evaluateWithThreeButtons();
+                        }
+                    } finally {
+                        setActiveOverlay(prevOverlayPkg, originalMode);
+                    }
+                }
+
+                public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
+                    base.evaluate();
+                }
+
+                private void evaluateWithThreeButtons() throws Throwable {
+                    setActiveOverlay(NAV_BAR_MODE_3BUTTON_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.THREE_BUTTON);
+                    evaluateWithoutChangingSetting(base);
+                }
+
+                private void evaluateWithTwoButtons() throws Throwable {
+                    setActiveOverlay(NAV_BAR_MODE_2BUTTON_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.TWO_BUTTON);
+                    base.evaluate();
+                }
+
+                private void evaluateWithZeroButtons() throws Throwable {
+                    setActiveOverlay(NAV_BAR_MODE_GESTURAL_OVERLAY,
+                            LauncherInstrumentation.NavigationModel.ZERO_BUTTON);
+                    base.evaluate();
+                }
+
+                private void setActiveOverlay(String overlayPackage,
+                        LauncherInstrumentation.NavigationModel expectedMode) throws Exception {
+                    setOverlayPackageEnabled(NAV_BAR_MODE_3BUTTON_OVERLAY,
+                            overlayPackage == NAV_BAR_MODE_3BUTTON_OVERLAY);
+                    setOverlayPackageEnabled(NAV_BAR_MODE_2BUTTON_OVERLAY,
+                            overlayPackage == NAV_BAR_MODE_2BUTTON_OVERLAY);
+                    setOverlayPackageEnabled(NAV_BAR_MODE_GESTURAL_OVERLAY,
+                            overlayPackage == NAV_BAR_MODE_GESTURAL_OVERLAY);
+
+                    for (int i = 0; i != 100; ++i) {
+                        if (mLauncher.getNavigationModel() == expectedMode) return;
+                        Thread.sleep(100);
+                    }
+                    Assert.fail("Couldn't switch to " + overlayPackage);
+                }
+
+                private void setOverlayPackageEnabled(String overlayPackage, boolean enable)
+                        throws Exception {
+                    Log.d(TAG, "setOverlayPackageEnabled: " + overlayPackage + " " + enable);
+                    final String action = enable ? "enable" : "disable";
+                    UiDevice.getInstance(getInstrumentation()).executeShellCommand(
+                            "cmd overlay " + action + " " + overlayPackage);
+                }
+            };
+        } else {
+            return base;
+        }
+    }
+}
diff --git a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java b/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
deleted file mode 100644
index f89842a..0000000
--- a/quickstep/tests/src/com/android/quickstep/QuickStepOnOffRule.java
+++ /dev/null
@@ -1,131 +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.quickstep.QuickStepOnOffRule.Mode.BOTH;
-import static com.android.quickstep.QuickStepOnOffRule.Mode.OFF;
-import static com.android.quickstep.QuickStepOnOffRule.Mode.ON;
-import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
-
-import static org.junit.Assert.assertTrue;
-
-import android.provider.Settings;
-import android.util.Log;
-
-import androidx.test.InstrumentationRegistry;
-
-import com.android.launcher3.tapl.LauncherInstrumentation;
-import com.android.launcher3.tapl.TestHelpers;
-
-import org.junit.rules.TestRule;
-import org.junit.runner.Description;
-import org.junit.runners.model.Statement;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.concurrent.Executor;
-
-/**
- * Test rule that allows executing a test with Quickstep on and then Quickstep off.
- * The test should be annotated with @QuickstepOnOff.
- */
-public class QuickStepOnOffRule implements TestRule {
-
-    static final String TAG = "QuickStepOnOffRule";
-
-    public enum Mode {
-        ON, OFF, BOTH
-    }
-
-    // Annotation for tests that need to be run with quickstep enabled and disabled.
-    @Retention(RetentionPolicy.RUNTIME)
-    @Target(ElementType.METHOD)
-    public @interface QuickstepOnOff {
-        Mode mode() default BOTH;
-    }
-
-    private final Executor mMainThreadExecutor;
-    private final LauncherInstrumentation mLauncher;
-
-    public QuickStepOnOffRule(Executor mainThreadExecutor, LauncherInstrumentation launcher) {
-        mLauncher = launcher;
-        this.mMainThreadExecutor = mainThreadExecutor;
-    }
-
-    @Override
-    public Statement apply(Statement base, Description description) {
-        if (TestHelpers.isInLauncherProcess() &&
-                description.getAnnotation(QuickstepOnOff.class) != null) {
-            Mode mode = description.getAnnotation(QuickstepOnOff.class).mode();
-            return new Statement() {
-                @Override
-                public void evaluate() throws Throwable {
-                    if (SwipeUpSetting.isSwipeUpSettingAvailable()) {
-                        try {
-                            if (mode == ON || mode == BOTH) {
-                                evaluateWithQuickstepOn();
-                            }
-                            if (mode == OFF || mode == BOTH) {
-                                evaluateWithQuickstepOff();
-                            }
-                        } finally {
-                            setSwipeUpSetting(null);
-                        }
-                    } else {
-                        // Execute without changing the setting, if the requested mode is
-                        // compatible.
-                        final boolean swipeUpEnabledDefaultValue =
-                                SwipeUpSetting.isSwipeUpEnabledDefaultValue();
-                        if (mode == BOTH ||
-                                mode == ON && swipeUpEnabledDefaultValue ||
-                                mode == OFF && !swipeUpEnabledDefaultValue) {
-                            evaluateWithoutChangingSetting(base);
-                        }
-                    }
-                }
-
-                public void setSwipeUpSetting(String value) {
-                    Log.d(TAG, "setSwipeUpSetting: " + value);
-                    assertTrue("Couldn't change Quickstep mode",
-                            Settings.Secure.putString(
-                                    InstrumentationRegistry.getInstrumentation().getTargetContext().
-                                            getContentResolver(),
-                                    SWIPE_UP_SETTING_NAME,
-                                    value));
-                }
-
-                public void evaluateWithoutChangingSetting(Statement base) throws Throwable {
-                    base.evaluate();
-                }
-
-                private void evaluateWithQuickstepOff() throws Throwable {
-                    setSwipeUpSetting("0");
-                    evaluateWithoutChangingSetting(base);
-                }
-
-                private void evaluateWithQuickstepOn() throws Throwable {
-                    setSwipeUpSetting("1");
-                    base.evaluate();
-                }
-            };
-        } else {
-            return base;
-        }
-    }
-}
diff --git a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
index 6031dcd..554aef4 100644
--- a/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
+++ b/quickstep/tests/src/com/android/quickstep/StartLauncherViaGestureTests.java
@@ -26,8 +26,8 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.util.RaceConditionReproducer;
-import com.android.quickstep.QuickStepOnOffRule.Mode;
-import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
+import com.android.quickstep.NavigationModeSwitchRule.Mode;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -62,7 +62,7 @@
 
     @Test
     @Ignore // Ignoring until gestural navigation event sequence settles
-    @QuickstepOnOff(mode = Mode.ON)
+    @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
     public void testPressHome() {
         runTest(enterEvt(Launcher.ON_CREATE_EVT),
                 exitEvt(Launcher.ON_CREATE_EVT),
@@ -77,14 +77,14 @@
 
     @Test
     @Ignore // Ignoring until gestural navigation event sequence settles
-    @QuickstepOnOff(mode = Mode.ON)
+    @NavigationModeSwitch(mode = Mode.TWO_BUTTON)
     public void testSwipeToOverview() {
         closeLauncherActivity();
         mLauncher.getBackground().switchToOverview();
     }
 
     @Test
-    @QuickstepOnOff
+    @NavigationModeSwitch
     public void testStressPressHome() {
         for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
             // Destroy Launcher activity.
@@ -96,7 +96,7 @@
     }
 
     @Test
-    @QuickstepOnOff
+    @NavigationModeSwitch
     public void testStressSwipeToOverview() {
         for (int i = 0; i < STRESS_REPEAT_COUNT; ++i) {
             // Destroy Launcher activity.
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
index c60cf45..226e5ee 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsQuickstep.java
@@ -37,7 +37,7 @@
 import com.android.launcher3.tapl.OverviewTask;
 import com.android.launcher3.tapl.TestHelpers;
 import com.android.launcher3.ui.TaplTestsLauncher3;
-import com.android.quickstep.QuickStepOnOffRule.QuickstepOnOff;
+import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
 import com.android.quickstep.views.RecentsView;
 
 import org.junit.Before;
@@ -79,7 +79,7 @@
     }
 
     @Test
-    @QuickstepOnOff
+    @NavigationModeSwitch
     @PortraitLandscape
     public void testWorkspaceSwitchToAllApps() {
         assertNotNull("switchToAllApps() returned null",
@@ -198,7 +198,7 @@
     }
 
     @Test
-    @QuickstepOnOff
+    @NavigationModeSwitch
     @PortraitLandscape
     public void testSwitchToOverview() throws Exception {
         assertNotNull("Workspace.switchToOverview() returned null",
@@ -208,7 +208,7 @@
     }
 
     @Test
-    @QuickstepOnOff
+    @NavigationModeSwitch
     @PortraitLandscape
     public void testBackground() throws Exception {
         startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 56ef414..19f537f 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -33,7 +33,7 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Fes doble toc i mantén premut per seleccionar un widget o per utilitzar les accions personalitzades."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d d\'amplada per %2$d d\'alçada"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Toca i mantén premut l\'element per col·locar-lo manualment"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"Mantén premut l\'element per afegir-lo manualment"</string>
     <string name="place_automatically" msgid="8064208734425456485">"Afegeix automàticament"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Cerca aplicacions"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"S\'estan carregant les aplicacions…"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index c58620e..58b65da 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -33,7 +33,7 @@
     <string name="long_accessible_way_to_add" msgid="4289502106628154155">"위젯을 선택하려면 두 번 탭한 다음 길게 터치하거나 맞춤 액션을 사용합니다."</string>
     <string name="widget_dims_format" msgid="2370757736025621599">"%1$d×%2$d"</string>
     <string name="widget_accessible_dims_format" msgid="3640149169885301790">"너비 %1$d, 높이 %2$d"</string>
-    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"길게 터치하여 직접 장소 추가"</string>
+    <string name="add_item_request_drag_hint" msgid="5899764264480397019">"길게 터치하여 직접 추가"</string>
     <string name="place_automatically" msgid="8064208734425456485">"자동으로 추가"</string>
     <string name="all_apps_search_bar_hint" msgid="1390553134053255246">"앱 검색"</string>
     <string name="all_apps_loading_message" msgid="5813968043155271636">"앱 로드 중…"</string>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index b2f3575..bc658e4 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -27,9 +27,6 @@
     <!-- Dynamic grid -->
     <dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
 
-    <dimen name="dynamic_grid_cell_layout_padding">0dp</dimen>
-    <dimen name="dynamic_grid_cell_layout_bottom_padding">5.5dp</dimen>
-
     <!-- Hotseat -->
     <dimen name="dynamic_grid_hotseat_side_padding">16dp</dimen>
 </resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b26b812..68a08a9 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -104,7 +104,7 @@
     <string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"Widgets de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="widgets_list" msgid="796804551140113767">"Lista de widgets"</string>
     <string name="widgets_list_closed" msgid="6141506579418771922">"Lista de widgets fechada."</string>
-    <string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar ao Ecrã principal"</string>
+    <string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar ao ecrã principal"</string>
     <string name="action_move_here" msgid="2170188780612570250">"Mover o item para aqui"</string>
     <string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado ao ecrã principal"</string>
     <string name="item_removed" msgid="851119963877842327">"Item removido"</string>
diff --git a/res/values/config.xml b/res/values/config.xml
index 6bd01b5..4b68b50 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -70,6 +70,7 @@
     <string name="instant_app_resolver_class" translatable="false"></string>
     <string name="main_process_initializer_class" translatable="false"></string>
     <string name="system_shortcut_factory_class" translatable="false"></string>
+    <string name="app_launch_tracker_class" translatable="false"></string>
 
     <!-- Package name of the default wallpaper picker. -->
     <string name="wallpaper_picker_package" translatable="false"></string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 04e4591..7822e05 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -22,13 +22,10 @@
     <dimen name="dynamic_grid_edge_margin">8dp</dimen>
     <dimen name="dynamic_grid_page_indicator_line_height">1dp</dimen>
     <dimen name="dynamic_grid_icon_drawable_padding">8dp</dimen>
-    <dimen name="dynamic_grid_workspace_top_padding">8dp</dimen>
-    <dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
     <!-- Minimum space between workspace and hotseat in spring loaded mode -->
     <dimen name="dynamic_grid_min_spring_loaded_space">8dp</dimen>
 
     <dimen name="dynamic_grid_cell_layout_padding">5.5dp</dimen>
-    <dimen name="dynamic_grid_cell_layout_bottom_padding">0dp</dimen>
     <dimen name="dynamic_grid_cell_padding_x">8dp</dimen>
 
     <!-- Hotseat -->
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index ff9dd13..18599ac 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_SEARCH;
+
 import android.app.ActivityOptions;
 import android.content.ActivityNotFoundException;
 import android.content.Intent;
@@ -32,10 +34,13 @@
 
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.shortcuts.DeepShortcutManager;
 import com.android.launcher3.uioverrides.DisplayRotationListener;
 import com.android.launcher3.uioverrides.WallpaperColorInfo;
 
+import androidx.annotation.Nullable;
+
 /**
  * Extension of BaseActivity allowing support for drag-n-drop
  */
@@ -148,7 +153,8 @@
 
     public abstract ActivityOptions getActivityLaunchOptions(View v);
 
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+    public boolean startActivitySafely(View v, Intent intent, @Nullable ItemInfo item,
+            @Nullable String sourceContainer) {
         if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
             Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
             return false;
@@ -169,13 +175,17 @@
                     && !((ShortcutInfo) item).isPromise();
             if (isShortcut) {
                 // Shortcuts need some special checks due to legacy reasons.
-                startShortcutIntentSafely(intent, optsBundle, item);
+                startShortcutIntentSafely(intent, optsBundle, item, sourceContainer);
             } else if (user == null || user.equals(Process.myUserHandle())) {
                 // Could be launching some bookkeeping activity
                 startActivity(intent, optsBundle);
+                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(),
+                        Process.myUserHandle(), sourceContainer);
             } else {
                 LauncherAppsCompat.getInstance(this).startActivityForProfile(
                         intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
+                AppLaunchTracker.INSTANCE.get(this).onStartApp(intent.getComponent(), user,
+                        sourceContainer);
             }
             getUserEventDispatcher().logAppLaunch(v, intent);
             getStatsLogManager().logAppLaunch(v, intent);
@@ -187,7 +197,8 @@
         return false;
     }
 
-    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info) {
+    private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
+            @Nullable String sourceContainer) {
         try {
             StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
             try {
@@ -202,6 +213,8 @@
                     String packageName = intent.getPackage();
                     DeepShortcutManager.getInstance(this).startShortcut(
                             packageName, id, intent.getSourceBounds(), optsBundle, info.user);
+                    AppLaunchTracker.INSTANCE.get(this).onStartShortcut(packageName, id, info.user,
+                            sourceContainer);
                 } else {
                     // Could be launching some bookkeeping activity
                     startActivity(intent, optsBundle);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index d75006e..4dd2e0a 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -480,7 +480,8 @@
      */
     public ObjectAnimator createTextAlphaAnimator(boolean fadeIn) {
         float toAlpha = shouldTextBeVisible() && fadeIn ? 1 : 0;
-        return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, toAlpha);
+        float fromAlpha = toAlpha == 1 ? 0 : 1f;
+        return ObjectAnimator.ofFloat(this, TEXT_ALPHA_PROPERTY, fromAlpha, toAlpha);
     }
 
     @Override
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7919d29..6397e14 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3;
 
-import android.appwidget.AppWidgetHostView;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -70,9 +68,6 @@
     public final int cellLayoutPaddingLeftRightPx;
     public final int cellLayoutBottomPaddingPx;
     public final int edgeMarginPx;
-    public final Rect defaultWidgetPadding;
-    public final int defaultPageSpacingPx;
-    private final int topWorkspacePadding;
     public float workspaceSpringLoadShrinkFactor;
     public final int workspaceSpringLoadedBottomSpace;
 
@@ -177,26 +172,25 @@
                 : Configuration.ORIENTATION_PORTRAIT);
         res = context.getResources();
 
-
-        ComponentName cn = new ComponentName(context.getPackageName(),
-                this.getClass().getName());
-        defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
         edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
         desiredWorkspaceLeftRightMarginPx = isVerticalBarLayout() ? 0 : edgeMarginPx;
+
         int cellLayoutPaddingLeftRightMultiplier = !isVerticalBarLayout() && isTablet
                 ? PORTRAIT_TABLET_LEFT_RIGHT_PADDING_MULTIPLIER : 1;
-        cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier *
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
-        cellLayoutBottomPaddingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_bottom_padding);
+        int cellLayoutPadding = res.getDimensionPixelSize(R.dimen.dynamic_grid_cell_layout_padding);
+        if (isLandscape) {
+            cellLayoutPaddingLeftRightPx = 0;
+            cellLayoutBottomPaddingPx = cellLayoutPadding;
+        } else {
+            cellLayoutPaddingLeftRightPx = cellLayoutPaddingLeftRightMultiplier * cellLayoutPadding;
+            cellLayoutBottomPaddingPx = 0;
+        }
+
         verticalDragHandleSizePx = res.getDimensionPixelSize(
                 R.dimen.vertical_drag_handle_size);
         verticalDragHandleOverlapWorkspace =
                 res.getDimensionPixelSize(R.dimen.vertical_drag_handle_overlap_workspace);
-        defaultPageSpacingPx =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_page_spacing);
-        topWorkspacePadding =
-                res.getDimensionPixelSize(R.dimen.dynamic_grid_workspace_top_padding);
+
         iconDrawablePaddingOriginalPx =
                 res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
         dropTargetBarSizePx = res.getDimensionPixelSize(R.dimen.dynamic_grid_drop_target_size);
@@ -360,7 +354,7 @@
 
         if (!isVerticalLayout) {
             int expectedWorkspaceHeight = availableHeightPx - hotseatBarSizePx
-                    - verticalDragHandleSizePx - topWorkspacePadding;
+                    - verticalDragHandleSizePx - edgeMarginPx;
             float minRequiredHeight = dropTargetBarSizePx + workspaceSpringLoadedBottomSpace;
             workspaceSpringLoadShrinkFactor = Math.min(
                     res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f,
@@ -471,15 +465,15 @@
                         ((inv.numColumns - 1) * cellWidthPx)));
                 availablePaddingX = (int) Math.min(availablePaddingX,
                         widthPx * MAX_HORIZONTAL_PADDING_PERCENT);
-                int availablePaddingY = Math.max(0, heightPx - topWorkspacePadding - paddingBottom
+                int availablePaddingY = Math.max(0, heightPx - edgeMarginPx - paddingBottom
                         - (2 * inv.numRows * cellHeightPx) - hotseatBarTopPaddingPx
                         - hotseatBarBottomPaddingPx);
-                padding.set(availablePaddingX / 2, topWorkspacePadding + availablePaddingY / 2,
+                padding.set(availablePaddingX / 2, edgeMarginPx + availablePaddingY / 2,
                         availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
             } else {
                 // Pad the top and bottom of the workspace with search/hotseat bar sizes
                 padding.set(desiredWorkspaceLeftRightMarginPx,
-                        topWorkspacePadding,
+                        edgeMarginPx,
                         desiredWorkspaceLeftRightMarginPx,
                         paddingBottom);
             }
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index d025a9b..0543e8d 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -104,7 +104,7 @@
                         / (2 * (grid.inv.numColumns + 1)))
                         + grid.edgeMarginPx;
             } else {
-                gap = grid.desiredWorkspaceLeftRightMarginPx - grid.defaultWidgetPadding.right;
+                gap = grid.desiredWorkspaceLeftRightMarginPx - grid.inv.defaultWidgetPadding.right;
             }
             lp.width = grid.availableWidthPx - 2 * gap;
 
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index f081303..c4495c7 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -20,7 +20,9 @@
 import static com.android.launcher3.Utilities.getDevicePrefs;
 
 import android.annotation.TargetApi;
+import android.appwidget.AppWidgetHostView;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -29,6 +31,7 @@
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -114,6 +117,7 @@
     public DeviceProfile portraitProfile;
 
     public Point defaultWallpaperSize;
+    public Rect defaultWidgetPadding;
 
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
     private ConfigMonitor mConfigMonitor;
@@ -235,6 +239,10 @@
         } else {
             defaultWallpaperSize = new Point(Math.max(smallSide * 2, largeSide), largeSide);
         }
+
+        ComponentName cn = new ComponentName(context.getPackageName(), getClass().getName());
+        defaultWidgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(context, cn, null);
+
         return closestProfile.name;
     }
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index f283a6c..867001a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -97,6 +97,7 @@
 import com.android.launcher3.logging.StatsLogUtils;
 import com.android.launcher3.logging.UserEventDispatcher;
 import com.android.launcher3.logging.UserEventDispatcher.UserEventDelegate;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.model.ModelWriter;
 import com.android.launcher3.notification.NotificationListener;
 import com.android.launcher3.popup.PopupContainerWithArrow;
@@ -694,7 +695,7 @@
 
             if (grantResults.length > 0
                     && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
-                startActivitySafely(v, intent, null);
+                startActivitySafely(v, intent, null, null);
             } else {
                 // TODO: Show a snack bar with link to settings
                 Toast.makeText(this, getString(R.string.msg_no_phone_permission,
@@ -798,6 +799,7 @@
             getUserEventDispatcher().startSession();
 
             UiFactory.onLauncherStateOrResumeChanged(this);
+            AppLaunchTracker.INSTANCE.get(this).onReturnedToHome();
         }
     }
 
@@ -1652,8 +1654,9 @@
         }
     }
 
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
-        boolean success = super.startActivitySafely(v, intent, item);
+    public boolean startActivitySafely(View v, Intent intent, ItemInfo item,
+            @Nullable String sourceContainer) {
+        boolean success = super.startActivitySafely(v, intent, item, sourceContainer);
         if (success && v instanceof BubbleTextView) {
             // This is set to the view that launched the activity that navigated the user away
             // from launcher. Since there is no callback for when the activity has finished
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 875288a..c65a871 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -18,22 +18,23 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO;
 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_PEEK_STATE_ORDINAL;
 import static com.android.launcher3.TestProtocol.OVERVIEW_STATE_ORDINAL;
+import static com.android.launcher3.TestProtocol.QUICK_SWITCH_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;
 
 import android.view.animation.Interpolator;
 
+import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.states.SpringLoadedState;
-import com.android.launcher3.uioverrides.AllAppsState;
-import com.android.launcher3.uioverrides.BackgroundAppState;
-import com.android.launcher3.uioverrides.OverviewState;
 import com.android.launcher3.uioverrides.UiFactory;
+import com.android.launcher3.uioverrides.states.AllAppsState;
+import com.android.launcher3.uioverrides.states.OverviewState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
@@ -77,7 +78,7 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[6];
+    private static final LauncherState[] sAllStates = new LauncherState[7];
 
     /**
      * TODO: Create a separate class for NORMAL state.
@@ -92,10 +93,15 @@
      */
     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 static final LauncherState OVERVIEW = new OverviewState(OVERVIEW_STATE_ORDINAL);
+    public static final LauncherState OVERVIEW_PEEK =
+            OverviewState.newPeekState(OVERVIEW_PEEK_STATE_ORDINAL);
+    public static final LauncherState QUICK_SWITCH =
+            OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL);
+    public static final LauncherState BACKGROUND_APP =
+            OverviewState.newBackgroundState(BACKGROUND_APP_STATE_ORDINAL);
 
     public final int ordinal;
 
@@ -183,17 +189,22 @@
         return Arrays.copyOf(sAllStates, sAllStates.length);
     }
 
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new float[] {1, 0, 0};
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1, 0, 0);
     }
 
-    public float[] getHotseatScaleAndTranslation(Launcher launcher) {
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
         // For most states, treat the hotseat as if it were part of the workspace.
         return getWorkspaceScaleAndTranslation(launcher);
     }
 
-    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
-        return new float[] {1.1f, 0f};
+    public ScaleAndTranslation getOverviewScaleAndTranslation(Launcher launcher) {
+        if (FeatureFlags.SWIPE_HOME.get()) {
+            float offscreenTranslationX = launcher.getDragLayer().getWidth()
+                    - launcher.getOverviewPanel().getPaddingStart();
+            return new ScaleAndTranslation(1f, offscreenTranslationX, 0f);
+        }
+        return new ScaleAndTranslation(1.1f, 0f, 0f);
     }
 
     public void onStateEnabled(Launcher launcher) {
@@ -281,4 +292,16 @@
 
         public abstract float getPageAlpha(int pageIndex);
     }
+
+    public static class ScaleAndTranslation {
+        public float scale;
+        public float translationX;
+        public float translationY;
+
+        public ScaleAndTranslation(float scale, float translationX, float translationY) {
+            this.scale = scale;
+            this.translationX = translationX;
+            this.translationY = translationY;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index f6b54f2..5f7538b 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -18,14 +18,20 @@
 
 import static android.view.View.VISIBLE;
 import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_PEEK;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_TRANSLATE_X;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
 import static com.android.launcher3.anim.Interpolators.ACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
+import static com.android.launcher3.anim.Interpolators.INSTANT;
+import static com.android.launcher3.anim.Interpolators.NEVER;
 import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_2;
+import static com.android.launcher3.anim.Interpolators.OVERSHOOT_1_7;
 import static com.android.launcher3.anim.Interpolators.clampToProgress;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
 
@@ -96,17 +102,21 @@
     // We separate the state animations into "atomic" and "non-atomic" components. The atomic
     // components may be run atomically - that is, all at once, instead of user-controlled. However,
     // atomic components are not restricted to this purpose; they can be user-controlled alongside
-    // non atomic components as well.
+    // non atomic components as well. Note that each gesture model has exactly one atomic component,
+    // ATOMIC_OVERVIEW_SCALE_COMPONENT *or* ATOMIC_OVERVIEW_PEEK_COMPONENT.
     @IntDef(flag = true, value = {
             NON_ATOMIC_COMPONENT,
-            ATOMIC_COMPONENT
+            ATOMIC_OVERVIEW_SCALE_COMPONENT,
+            ATOMIC_OVERVIEW_PEEK_COMPONENT,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimationComponents {}
     public static final int NON_ATOMIC_COMPONENT = 1 << 0;
-    public static final int ATOMIC_COMPONENT = 1 << 1;
+    public static final int ATOMIC_OVERVIEW_SCALE_COMPONENT = 1 << 1;
+    public static final int ATOMIC_OVERVIEW_PEEK_COMPONENT = 1 << 2;
 
-    public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_COMPONENT;
+    public static final int ANIM_ALL = NON_ATOMIC_COMPONENT | ATOMIC_OVERVIEW_SCALE_COMPONENT
+            | ATOMIC_OVERVIEW_PEEK_COMPONENT;
 
     private final AnimationConfig mConfig = new AnimationConfig();
     private final Handler mUiHandler;
@@ -282,18 +292,20 @@
      */
     public void prepareForAtomicAnimation(LauncherState fromState, LauncherState toState,
             AnimatorSetBuilder builder) {
-        if (fromState == NORMAL && toState.overviewUi) {
+        if (fromState == NORMAL && toState == OVERVIEW) {
             builder.setInterpolator(ANIM_WORKSPACE_SCALE, OVERSHOOT_1_2);
             builder.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
             builder.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
+            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, OVERSHOOT_1_7);
             builder.setInterpolator(ANIM_OVERVIEW_FADE, OVERSHOOT_1_2);
 
             // Start from a higher overview scale, but only if we're invisible so we don't jump.
             UiFactory.prepareToShowOverview(mLauncher);
-        } else if (fromState.overviewUi && toState == NORMAL) {
+        } else if (fromState == OVERVIEW && toState == NORMAL) {
             builder.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
             builder.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
             builder.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
+            builder.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
             builder.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
             Workspace workspace = mLauncher.getWorkspace();
 
@@ -311,9 +323,25 @@
                 workspace.getHotseat().setScaleX(0.92f);
                 workspace.getHotseat().setScaleY(0.92f);
             }
+        } else if (fromState == NORMAL && toState == OVERVIEW_PEEK) {
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, INSTANT);
+        } else if (fromState == OVERVIEW_PEEK && toState == NORMAL) {
+            builder.setInterpolator(ANIM_OVERVIEW_FADE, NEVER);
         }
     }
 
+    public AnimatorSet createAtomicAnimation(LauncherState fromState, LauncherState toState,
+            AnimatorSetBuilder builder, @AnimationComponents int atomicComponent, long duration) {
+        prepareForAtomicAnimation(fromState, toState, builder);
+        AnimationConfig config = new AnimationConfig();
+        config.animComponents = atomicComponent;
+        config.duration = duration;
+        for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
+            handler.setStateWithAnimation(toState, builder, config);
+        }
+        return builder.build();
+    }
+
     /**
      * Creates a {@link AnimatorPlaybackController} that can be used for a controlled
      * state transition. The UI is force-set to fromState before creating the controller.
@@ -373,7 +401,6 @@
             AnimatorSetBuilder builder, final Runnable onCompleteRunnable) {
 
         for (StateHandler handler : getStateHandlers()) {
-            builder.startTag(handler);
             handler.setStateWithAnimation(state, builder, mConfig);
         }
 
@@ -594,8 +621,12 @@
             mCurrentAnimation.addListener(this);
         }
 
-        public boolean playAtomicComponent() {
-            return (animComponents & ATOMIC_COMPONENT) != 0;
+        public boolean playAtomicOverviewScaleComponent() {
+            return (animComponents & ATOMIC_OVERVIEW_SCALE_COMPONENT) != 0;
+        }
+
+        public boolean playAtomicOverviewPeekComponent() {
+            return (animComponents & ATOMIC_OVERVIEW_PEEK_COMPONENT) != 0;
         }
 
         public boolean playNonAtomicComponent() {
diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index 4eb3627..49a736e 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -28,8 +28,10 @@
     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;
+    public static final int OVERVIEW_PEEK_STATE_ORDINAL = 3;
+    public static final int QUICK_SWITCH_STATE_ORDINAL = 4;
+    public static final int ALL_APPS_STATE_ORDINAL = 5;
+    public static final int BACKGROUND_APP_STATE_ORDINAL = 6;
 
     public static final String TEST_INFO_RESPONSE_FIELD = "response";
     public static final String REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT =
@@ -40,4 +42,7 @@
             "all-apps-to-overview-swipe-height";
     public static final String REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT =
             "home-to-all-apps-swipe-height";
+
+    public static boolean sDebugTracing = false;
+    public static final String NO_DRAG_TAG = "b/129434166";
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index dd755cb..fd4b508 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -308,10 +308,14 @@
             Log.e(TAG, "mapToRange: range has 0 length");
             return toMin;
         }
-        float progress = Math.abs(t - fromMin) / Math.abs(fromMax - fromMin);
+        float progress = getProgress(t, fromMin, fromMax);
         return mapRange(interpolator.getInterpolation(progress), toMin, toMax);
     }
 
+    public static float getProgress(float current, float min, float max) {
+        return Math.abs(current - min) / Math.abs(max - min);
+    }
+
     public static float mapRange(float value, float min, float max) {
         return min + (value * (max - min));
     }
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 720a692..d24a5a6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -298,12 +298,12 @@
 
         if (grid.shouldFadeAdjacentWorkspaceScreens()) {
             // In landscape mode the page spacing is set to the default.
-            setPageSpacing(grid.defaultPageSpacingPx);
+            setPageSpacing(grid.edgeMarginPx);
         } else {
             // In portrait, we want the pages spaced such that there is no
             // overhang of the previous / next page into the current page viewport.
             // We assume symmetrical padding in portrait mode.
-            setPageSpacing(Math.max(grid.defaultPageSpacingPx, padding.left + 1));
+            setPageSpacing(Math.max(grid.edgeMarginPx, padding.left + 1));
         }
 
         int paddingLeftRight = grid.cellLayoutPaddingLeftRightPx;
@@ -1446,6 +1446,10 @@
 
     public DragView beginDragShared(View child, DragSource source, ItemInfo dragObject,
             DragPreviewProvider previewProvider, DragOptions dragOptions) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "beginDragShared");
+        }
         float iconScale = 1f;
         if (child instanceof BubbleTextView) {
             Drawable icon = ((BubbleTextView) child).getIcon();
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 0507470..99a8801 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.LauncherState.HOTSEAT_ICONS;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_FADE;
 import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_SCALE;
+import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_WORKSPACE_TRANSLATE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.ZOOM_OUT;
 import static com.android.launcher3.anim.PropertySetter.NO_ANIM_PROPERTY_SETTER;
@@ -31,6 +32,7 @@
 import android.view.animation.Interpolator;
 
 import com.android.launcher3.LauncherState.PageAlphaProvider;
+import com.android.launcher3.LauncherState.ScaleAndTranslation;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
@@ -71,9 +73,10 @@
      */
     private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter,
             AnimatorSetBuilder builder, AnimationConfig config) {
-        float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
-        float[] hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(mLauncher);
-        mNewScale = scaleAndTranslation[0];
+        ScaleAndTranslation scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
+        ScaleAndTranslation hotseatScaleAndTranslation = state.getHotseatScaleAndTranslation(
+                mLauncher);
+        mNewScale = scaleAndTranslation.scale;
         PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
         final int childCount = mWorkspace.getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -84,7 +87,7 @@
         int elements = state.getVisibleElements(mLauncher);
         Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
                 pageAlphaProvider.interpolator);
-        boolean playAtomicComponent = config.playAtomicComponent();
+        boolean playAtomicComponent = config.playAtomicOverviewScaleComponent();
         Hotseat hotseat = mWorkspace.getHotseat();
         if (playAtomicComponent) {
             Interpolator scaleInterpolator = builder.getInterpolator(ANIM_WORKSPACE_SCALE, ZOOM_OUT);
@@ -98,7 +101,7 @@
             dragLayer.mapCoordInSelfToDescendant(hotseat, workspacePivot);
             hotseat.setPivotX(workspacePivot[0]);
             hotseat.setPivotY(workspacePivot[1]);
-            float hotseatScale = hotseatScaleAndTranslation[0];
+            float hotseatScale = hotseatScaleAndTranslation.scale;
             propertySetter.setFloat(hotseat, SCALE_PROPERTY, hotseatScale, scaleInterpolator);
 
             float hotseatIconsAlpha = (elements & HOTSEAT_ICONS) != 0 ? 1 : 0;
@@ -112,16 +115,18 @@
             return;
         }
 
-        Interpolator translationInterpolator = !playAtomicComponent ? LINEAR : ZOOM_OUT;
+        Interpolator translationInterpolator = !playAtomicComponent
+                ? LINEAR
+                : builder.getInterpolator(ANIM_WORKSPACE_TRANSLATE, ZOOM_OUT);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
-                scaleAndTranslation[1], translationInterpolator);
+                scaleAndTranslation.translationX, translationInterpolator);
         propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
-                scaleAndTranslation[2], translationInterpolator);
+                scaleAndTranslation.translationY, translationInterpolator);
 
         propertySetter.setFloat(hotseat, View.TRANSLATION_Y,
-                hotseatScaleAndTranslation[2], translationInterpolator);
+                hotseatScaleAndTranslation.translationY, translationInterpolator);
         propertySetter.setFloat(mWorkspace.getPageIndicator(), View.TRANSLATION_Y,
-                hotseatScaleAndTranslation[2], translationInterpolator);
+                hotseatScaleAndTranslation.translationY, translationInterpolator);
 
         // Set scrim
         WorkspaceAndHotseatScrim scrim = mLauncher.getDragLayer().getScrim();
@@ -145,7 +150,7 @@
             propertySetter.setInt(cl.getScrimBackground(),
                     DRAWABLE_ALPHA, drawableAlpha, ZOOM_OUT);
         }
-        if (config.playAtomicComponent()) {
+        if (config.playAtomicOverviewScaleComponent()) {
             Interpolator fadeInterpolator = builder.getInterpolator(ANIM_WORKSPACE_FADE,
                     pageAlphaProvider.interpolator);
             propertySetter.setFloat(cl.getShortcutsAndWidgets(), View.ALPHA,
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 69b4bdb..3cfa0b1 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -32,6 +32,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.allapps.AlphabeticalAppsList.AdapterItem;
 import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.touch.ItemClickHandler;
 import com.android.launcher3.touch.ItemLongClickListener;
 import com.android.launcher3.util.PackageManagerHelper;
@@ -263,12 +264,8 @@
             case VIEW_TYPE_SEARCH_MARKET:
                 View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
                         parent, false);
-                searchMarketView.setOnClickListener(new View.OnClickListener() {
-                    @Override
-                    public void onClick(View v) {
-                        mLauncher.startActivitySafely(v, mMarketSearchIntent, null);
-                    }
-                });
+                searchMarketView.setOnClickListener(v -> mLauncher.startActivitySafely(
+                        v, mMarketSearchIntent, null, AppLaunchTracker.CONTAINER_SEARCH));
                 return new ViewHolder(searchMarketView);
             case VIEW_TYPE_ALL_APPS_DIVIDER:
                 return new ViewHolder(mLayoutInflater.inflate(
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a4ecec7..4a1d432 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -205,7 +205,7 @@
         mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
 
         setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
-                (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, LINEAR);
+                (visibleElements & VERTICAL_SWIPE_INDICATOR) != 0 ? 255 : 0, allAppsFade);
     }
 
     public AnimatorListenerAdapter getProgressAnimatorListener() {
diff --git a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
index 91be504..4515dde 100644
--- a/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/search/AllAppsSearchBarController.java
@@ -28,6 +28,7 @@
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.model.AppLaunchTracker;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.PackageManagerHelper;
 
@@ -111,7 +112,8 @@
             return false;
         }
         return mLauncher.startActivitySafely(v,
-                PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null);
+                PackageManagerHelper.getMarketSearchIntent(mLauncher, query), null,
+                AppLaunchTracker.CONTAINER_SEARCH);
     }
 
     @Override
diff --git a/src/com/android/launcher3/anim/AnimatorSetBuilder.java b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
index fdac5c8..5c498f8 100644
--- a/src/com/android/launcher3/anim/AnimatorSetBuilder.java
+++ b/src/com/android/launcher3/anim/AnimatorSetBuilder.java
@@ -30,20 +30,21 @@
 
     public static final int ANIM_VERTICAL_PROGRESS = 0;
     public static final int ANIM_WORKSPACE_SCALE = 1;
-    public static final int ANIM_WORKSPACE_FADE = 2;
-    public static final int ANIM_OVERVIEW_SCALE = 3;
-    public static final int ANIM_OVERVIEW_FADE = 4;
-    public static final int ANIM_ALL_APPS_FADE = 5;
+    public static final int ANIM_WORKSPACE_TRANSLATE = 2;
+    public static final int ANIM_WORKSPACE_FADE = 3;
+    public static final int ANIM_OVERVIEW_SCALE = 4;
+    public static final int ANIM_OVERVIEW_TRANSLATE_X = 5;
+    public static final int ANIM_OVERVIEW_TRANSLATE_Y = 6;
+    public static final int ANIM_OVERVIEW_FADE = 7;
+    public static final int ANIM_ALL_APPS_FADE = 8;
+
+    public static final int FLAG_DONT_ANIMATE_OVERVIEW = 1 << 0;
 
     protected final ArrayList<Animator> mAnims = new ArrayList<>();
 
     private final SparseArray<Interpolator> mInterpolators = new SparseArray<>();
     private List<Runnable> mOnFinishRunnables = new ArrayList<>();
-
-    /**
-     * Associates a tag with all the animations added after this call.
-     */
-    public void startTag(Object obj) { }
+    private int mFlags = 0;
 
     public void play(Animator anim) {
         mAnims.add(anim);
@@ -77,4 +78,12 @@
     public void setInterpolator(int animId, Interpolator interpolator) {
         mInterpolators.put(animId, interpolator);
     }
+
+    public void addFlag(int flag) {
+        mFlags |= flag;
+    }
+
+    public boolean hasFlag(int flag) {
+        return (mFlags & flag) != 0;
+    }
 }
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 675e26d..217b6db 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -57,6 +57,9 @@
 
     public static final Interpolator EXAGGERATED_EASE;
 
+    public static final Interpolator INSTANT = t -> 1;
+    public static final Interpolator NEVER = t -> 0;
+
     private static final int MIN_SETTLE_DURATION = 200;
     private static final float OVERSHOOT_FACTOR = 0.9f;
 
@@ -69,6 +72,7 @@
     }
 
     public static final Interpolator OVERSHOOT_1_2 = new OvershootInterpolator(1.2f);
+    public static final Interpolator OVERSHOOT_1_7 = new OvershootInterpolator(1.7f);
 
     public static final Interpolator TOUCH_RESPONSE_INTERPOLATOR =
             new PathInterpolator(0.3f, 0f, 0.1f, 1f);
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index c260fa3..106d901 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -180,7 +180,7 @@
             currentValue = getFromStorage(context, defaultValue);
         }
 
-        void updateStorage(Context context, boolean value) {
+        public void updateStorage(Context context, boolean value) {
             SharedPreferences.Editor editor = context.getSharedPreferences(FLAGS_PREF_NAME,
                     Context.MODE_PRIVATE).edit();
             if (value == defaultValue) {
@@ -272,7 +272,7 @@
         }
 
         @Override
-        void updateStorage(Context context, boolean value) {
+        public void updateStorage(Context context, boolean value) {
             if (contentResolver == null) {
                 return;
             }
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 03dc66e..8b100d9 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -219,6 +219,9 @@
     }
 
     private void callOnDragStart() {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, "callOnDragStart");
+        }
         if (mOptions.preDragCondition != null) {
             mOptions.preDragCondition.onPreDragEnd(mDragObject, true /* dragStarted*/);
         }
@@ -472,6 +475,9 @@
     }
 
     private void handleMoveEvent(int x, int y) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG, "handleMoveEvent1");
+        }
         mDragObject.dragView.move(x, y);
 
         // Drop on someone?
@@ -488,6 +494,10 @@
 
         if (mIsInPreDrag && mOptions.preDragCondition != null
                 && mOptions.preDragCondition.shouldStartDrag(mDistanceSinceScroll)) {
+            if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                        "handleMoveEvent2");
+            }
             callOnDragStart();
         }
     }
@@ -525,6 +535,10 @@
      * Call this from a drag source view.
      */
     public boolean onControllerTouchEvent(MotionEvent ev) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "onControllerTouchEvent1");
+        }
         if (mDragDriver == null || mOptions == null || mOptions.isAccessibleDrag) {
             return false;
         }
@@ -545,6 +559,10 @@
                 break;
         }
 
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "onControllerTouchEvent2");
+        }
         return mDragDriver.onTouchEvent(ev);
     }
 
diff --git a/src/com/android/launcher3/dragndrop/DragDriver.java b/src/com/android/launcher3/dragndrop/DragDriver.java
index 84fc94d..551f2d0 100644
--- a/src/com/android/launcher3/dragndrop/DragDriver.java
+++ b/src/com/android/launcher3/dragndrop/DragDriver.java
@@ -45,10 +45,18 @@
     public void onDragViewAnimationEnd() { }
 
     public boolean onTouchEvent(MotionEvent ev) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "onTouchEvent " + ev);
+        }
         final int action = ev.getAction();
 
         switch (action) {
             case MotionEvent.ACTION_MOVE:
+                if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                    android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                            "onTouchEvent MOVE");
+                }
                 mEventListener.onDriverDragMove(ev.getX(), ev.getY());
                 break;
             case MotionEvent.ACTION_UP:
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 6950a1f..9f902ed 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -126,6 +126,10 @@
     protected boolean findActiveController(MotionEvent ev) {
         if (mActivity.getStateManager().getState().disableInteraction) {
             // You Shall Not Pass!!!
+            if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                        "mActiveController = null");
+            }
             mActiveController = null;
             return true;
         }
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 7a14b36..bcddd03 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -618,8 +618,7 @@
             mFolderIcon.setBackgroundVisible(true);
             mFolderIcon.mFolderName.setTextVisibility(true);
             if (wasAnimated) {
-                mFolderIcon.mBackground.fadeInBackgroundShadow();
-                mFolderIcon.mBackground.animateBackgroundStroke();
+                mFolderIcon.animateBgShadowAndStroke();
                 mFolderIcon.onFolderClose(mContent.getCurrentPage());
                 if (mFolderIcon.hasDot()) {
                     mFolderIcon.animateDotScale(0f, 1f);
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 71c9148..bcd5701 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -189,7 +189,17 @@
         return icon;
     }
 
+    public void animateBgShadowAndStroke() {
+        mBackground.fadeInBackgroundShadow();
+        mBackground.animateBackgroundStroke();
+    }
+
+    public BubbleTextView getFolderName() {
+        return mFolderName;
+    }
+
     public void getPreviewBounds(Rect outBounds) {
+        mPreviewItemManager.recomputePreviewDrawingParams();
         mBackground.getBounds(outBounds);
     }
 
diff --git a/src/com/android/launcher3/model/AppLaunchTracker.java b/src/com/android/launcher3/model/AppLaunchTracker.java
new file mode 100644
index 0000000..1613d47
--- /dev/null
+++ b/src/com/android/launcher3/model/AppLaunchTracker.java
@@ -0,0 +1,56 @@
+/*
+ * 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.model;
+
+import static com.android.launcher3.util.ResourceBasedOverride.Overrides.getObject;
+
+import android.content.ComponentName;
+import android.os.UserHandle;
+
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.launcher3.util.MainThreadInitializedObject;
+import com.android.launcher3.util.ResourceBasedOverride;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Callback for receiving various app launch events
+ */
+public class AppLaunchTracker implements ResourceBasedOverride {
+
+    /**
+     * Derived from LauncherEvent proto.
+     * TODO: Use proper descriptive constants
+     */
+    public static final String CONTAINER_DEFAULT = Integer.toString(ContainerType.WORKSPACE);
+    public static final String CONTAINER_ALL_APPS = Integer.toString(ContainerType.ALLAPPS);
+    public static final String CONTAINER_PREDICTIONS = Integer.toString(ContainerType.PREDICTION);
+    public static final String CONTAINER_SEARCH = Integer.toString(ContainerType.SEARCHRESULT);
+
+
+    public static final MainThreadInitializedObject<AppLaunchTracker> INSTANCE =
+            new MainThreadInitializedObject<>(c ->
+                    getObject(AppLaunchTracker.class, c, R.string.app_launch_tracker_class));
+
+    public void onStartShortcut(String packageName, String shortcutId, UserHandle user,
+            @Nullable String container) { }
+
+    public void onStartApp(ComponentName componentName, UserHandle user,
+            @Nullable String container) { }
+
+    public void onReturnedToHome() { }
+}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 10ecc4f..080a0cb 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -187,6 +187,10 @@
      * @return the container if shown or null.
      */
     public static PopupContainerWithArrow showForIcon(BubbleTextView icon) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "PopupContainerWithArrow.showForIcon");
+        }
         Launcher launcher = Launcher.getLauncher(icon.getContext());
         if (getOpen(launcher) != null) {
             // There is already an items container open, so don't open this one.
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index fdc1b39..e7b8292 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -195,7 +195,7 @@
             return view -> {
                 Intent intent = new PackageManagerHelper(view.getContext()).getMarketIntent(
                         itemInfo.getTargetComponent().getPackageName());
-                activity.startActivitySafely(view, intent, itemInfo);
+                activity.startActivitySafely(view, intent, itemInfo, null);
                 AbstractFloatingView.closeAllOpenViews(activity);
             };
         }
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index fcace98..be3e6c9 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -41,7 +41,7 @@
     }
 
     @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
         DeviceProfile grid = launcher.getDeviceProfile();
         Workspace ws = launcher.getWorkspace();
         if (ws.getChildCount() == 0) {
@@ -50,7 +50,7 @@
 
         if (grid.isVerticalBarLayout()) {
             float scale = grid.workspaceSpringLoadShrinkFactor;
-            return new float[] {scale, 0, 0};
+            return new ScaleAndTranslation(scale, 0, 0);
         }
 
         float scale = grid.workspaceSpringLoadShrinkFactor;
@@ -69,12 +69,12 @@
         float myCenter = ws.getTop() + halfHeight;
         float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
         float actualCellTop = myCenter - cellTopFromCenter * scale;
-        return new float[] { scale, 0, (desiredCellTop - actualCellTop) / scale};
+        return new ScaleAndTranslation(scale, 0, (desiredCellTop - actualCellTop) / scale);
     }
 
     @Override
-    public float[] getHotseatScaleAndTranslation(Launcher launcher) {
-        return new float[] {1, 0, 0};
+    public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1, 0, 0);
     }
 
     @Override
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c125c10..c403e76 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -20,7 +20,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherStateManager.ANIM_ALL;
-import static com.android.launcher3.LauncherStateManager.ATOMIC_COMPONENT;
+import static com.android.launcher3.LauncherStateManager.ATOMIC_OVERVIEW_SCALE_COMPONENT;
 import static com.android.launcher3.LauncherStateManager.NON_ATOMIC_COMPONENT;
 import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
 import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
@@ -38,9 +38,6 @@
 import com.android.launcher3.LauncherAnimUtils;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationComponents;
-import com.android.launcher3.LauncherStateManager.AnimationConfig;
-import com.android.launcher3.LauncherStateManager.StateHandler;
-import com.android.launcher3.TestProtocol;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.AnimationSuccessListener;
 import com.android.launcher3.anim.AnimatorPlaybackController;
@@ -68,10 +65,11 @@
      * Play an atomic recents animation when the progress from NORMAL to OVERVIEW reaches this.
      */
     public static final float ATOMIC_OVERVIEW_ANIM_THRESHOLD = 0.5f;
-    protected static final long ATOMIC_DURATION = 200;
+    protected final long ATOMIC_DURATION = getAtomicDuration();
 
     protected final Launcher mLauncher;
     protected final SwipeDetector mDetector;
+    protected final SwipeDetector.Direction mSwipeDirection;
 
     private boolean mNoIntercept;
     protected int mStartContainerType;
@@ -108,6 +106,11 @@
     public AbstractStateChangeTouchController(Launcher l, SwipeDetector.Direction dir) {
         mLauncher = l;
         mDetector = new SwipeDetector(l, this, dir);
+        mSwipeDirection = dir;
+    }
+
+    protected long getAtomicDuration() {
+        return 200;
     }
 
     protected abstract boolean canInterceptTouch(MotionEvent ev);
@@ -214,7 +217,7 @@
         }
 
         if (mAtomicComponentsController != null) {
-            animComponents &= ~ATOMIC_COMPONENT;
+            animComponents &= ~ATOMIC_OVERVIEW_SCALE_COMPONENT;
         }
         mProgressMultiplier = initCurrentAnimation(animComponents);
         mCurrentAnimation.dispatchOnStart();
@@ -229,6 +232,11 @@
 
     @Override
     public void onDragStart(boolean start) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "AbstractStateChangeTouchController.onDragStart() called with: start = [" +
+                            start + "]");
+        }
         mStartState = mLauncher.getStateManager().getState();
         if (mStartState == ALL_APPS) {
             mStartContainerType = LauncherLogProto.ContainerType.ALLAPPS;
@@ -260,8 +268,14 @@
     public boolean onDrag(float displacement) {
         float deltaProgress = mProgressMultiplier * (displacement - mDisplacementShift);
         float progress = deltaProgress + mStartProgress;
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "AbstractStateChangeTouchController.onDrag() called with: displacement = [" +
+                            displacement + "], progress = [" + progress + "]");
+        }
         updateProgress(progress);
-        boolean isDragTowardPositive = (displacement - mDisplacementShift) < 0;
+        boolean isDragTowardPositive = mSwipeDirection.isPositive(
+                displacement - mDisplacementShift);
         if (progress <= 0) {
             if (reinitCurrentAnimation(false, isDragTowardPositive)) {
                 mDisplacementShift = displacement;
@@ -297,7 +311,7 @@
      * When going between normal and overview states, see if we passed the overview threshold and
      * play the appropriate atomic animation if so.
      */
-    protected void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
+    private void maybeUpdateAtomicAnim(LauncherState fromState, LauncherState toState,
             float progress) {
         if (!goingBetweenNormalAndOverview(fromState, toState)) {
             return;
@@ -347,14 +361,8 @@
     private AnimatorSet createAtomicAnimForState(LauncherState fromState, LauncherState targetState,
             long duration) {
         AnimatorSetBuilder builder = getAnimatorSetBuilderForStates(fromState, targetState);
-        mLauncher.getStateManager().prepareForAtomicAnimation(fromState, targetState, builder);
-        AnimationConfig config = new AnimationConfig();
-        config.animComponents = ATOMIC_COMPONENT;
-        config.duration = duration;
-        for (StateHandler handler : mLauncher.getStateManager().getStateHandlers()) {
-            handler.setStateWithAnimation(targetState, builder, config);
-        }
-        return builder.build();
+        return mLauncher.getStateManager().createAtomicAnimation(fromState, targetState, builder,
+                ATOMIC_OVERVIEW_SCALE_COMPONENT, duration);
     }
 
     protected AnimatorSetBuilder getAnimatorSetBuilderForStates(LauncherState fromState,
@@ -384,6 +392,12 @@
                     ? MIN_PROGRESS_TO_ALL_APPS : SUCCESS_TRANSITION_PROGRESS;
             targetState = (interpolatedProgress > successProgress) ? mToState : mFromState;
         }
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "AbstractStateChangeTouchController.onDragEnd() called with: velocity = [" +
+                            velocity + "], fling = [" + fling + "], target state: " +
+                            targetState.getClass().getSimpleName());
+        }
 
         final float endProgress;
         final float startProgress;
@@ -434,11 +448,7 @@
             mLauncher.getAppsView().addSpringFromFlingUpdateListener(anim, velocity);
         }
         anim.start();
-        settleAtomicAnimation(endProgress, anim.getDuration());
-    }
-
-    protected void settleAtomicAnimation(float endProgress, long duration) {
-        mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, duration);
+        mAtomicAnimAutoPlayInfo = new AutoPlayAtomicAnimationInfo(endProgress, anim.getDuration());
         maybeAutoPlayAtomicComponentsAnim();
     }
 
diff --git a/src/com/android/launcher3/touch/ItemClickHandler.java b/src/com/android/launcher3/touch/ItemClickHandler.java
index 52fef9f..3c77860 100644
--- a/src/com/android/launcher3/touch/ItemClickHandler.java
+++ b/src/com/android/launcher3/touch/ItemClickHandler.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.ItemInfoWithIcon.FLAG_DISABLED_SUSPENDED;
 import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
 import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
+import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
 
 import android.app.AlertDialog;
 import android.content.Intent;
@@ -31,6 +32,8 @@
 import android.view.View.OnClickListener;
 import android.widget.Toast;
 
+import androidx.annotation.Nullable;
+
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.BubbleTextView;
 import com.android.launcher3.FolderInfo;
@@ -56,9 +59,21 @@
     /**
      * Instance used for click handling on items
      */
-    public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
+    public static final OnClickListener INSTANCE = getInstance(null);
 
-    private static void onClick(View v) {
+    public static final OnClickListener getInstance(String sourceContainer) {
+        return v -> onClick(v, sourceContainer);
+    }
+
+    private static void onClick(View v, String sourceContainer) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "onClick() called with: v = [" + v.getClass().getSimpleName() +
+                            "], sourceContainer = [" +
+                            (sourceContainer != null ?
+                                    sourceContainer.getClass().getSimpleName() : "null")
+                            + "]");
+        }
         // Make sure that rogue clicks don't get through while allapps is launching, or after the
         // view has detached (it's possible for this to happen if the view is removed mid touch).
         if (v.getWindowToken() == null) {
@@ -72,13 +87,14 @@
 
         Object tag = v.getTag();
         if (tag instanceof ShortcutInfo) {
-            onClickAppShortcut(v, (ShortcutInfo) tag, launcher);
+            onClickAppShortcut(v, (ShortcutInfo) tag, launcher, sourceContainer);
         } else if (tag instanceof FolderInfo) {
             if (v instanceof FolderIcon) {
                 onClickFolderIcon(v);
             }
         } else if (tag instanceof AppInfo) {
-            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
+            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher,
+                    sourceContainer == null ? CONTAINER_ALL_APPS: sourceContainer);
         } else if (tag instanceof LauncherAppWidgetInfo) {
             if (v instanceof PendingAppWidgetHostView) {
                 onClickPendingWidget((PendingAppWidgetHostView) v, launcher);
@@ -154,7 +170,7 @@
     private static void startMarketIntentForPackage(View v, Launcher launcher, String packageName) {
         ItemInfo item = (ItemInfo) v.getTag();
         Intent intent = new PackageManagerHelper(launcher).getMarketIntent(packageName);
-        launcher.startActivitySafely(v, intent, item);
+        launcher.startActivitySafely(v, intent, item, null);
     }
 
     /**
@@ -162,7 +178,8 @@
      *
      * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
      */
-    public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher) {
+    public static void onClickAppShortcut(View v, ShortcutInfo shortcut, Launcher launcher,
+            @Nullable String sourceContainer) {
         if (shortcut.isDisabled()) {
             final int disabledFlags = shortcut.runtimeStatusFlags & ShortcutInfo.FLAG_DISABLED_MASK;
             if ((disabledFlags &
@@ -201,10 +218,11 @@
         }
 
         // Start activities
-        startAppShortcutOrInfoActivity(v, shortcut, launcher);
+        startAppShortcutOrInfoActivity(v, shortcut, launcher, sourceContainer);
     }
 
-    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher) {
+    private static void startAppShortcutOrInfoActivity(View v, ItemInfo item, Launcher launcher,
+            @Nullable String sourceContainer) {
         Intent intent;
         if (item instanceof PromiseAppInfo) {
             PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
@@ -227,6 +245,6 @@
                 intent.setPackage(null);
             }
         }
-        launcher.startActivitySafely(v, intent, item);
+        launcher.startActivitySafely(v, intent, item, sourceContainer);
     }
 }
diff --git a/src/com/android/launcher3/touch/ItemLongClickListener.java b/src/com/android/launcher3/touch/ItemLongClickListener.java
index babbcdd..003b442 100644
--- a/src/com/android/launcher3/touch/ItemLongClickListener.java
+++ b/src/com/android/launcher3/touch/ItemLongClickListener.java
@@ -74,6 +74,10 @@
     }
 
     private static boolean onAllAppsItemLongClick(View v) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "onAllAppsItemLongClick");
+        }
         Launcher launcher = Launcher.getLauncher(v.getContext());
         if (!canStartDrag(launcher)) return false;
         // When we have exited all apps or are in transition, disregard long clicks
diff --git a/src/com/android/launcher3/touch/SwipeDetector.java b/src/com/android/launcher3/touch/SwipeDetector.java
index a0a410e..e558fc7 100644
--- a/src/com/android/launcher3/touch/SwipeDetector.java
+++ b/src/com/android/launcher3/touch/SwipeDetector.java
@@ -24,6 +24,8 @@
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
+import com.android.launcher3.Utilities;
+
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
 
@@ -64,20 +66,25 @@
 
     public static abstract class Direction {
 
-        abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint);
+        abstract float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint,
+                boolean isRtl);
 
         /**
          * Distance in pixels a touch can wander before we think the user is scrolling.
          */
         abstract float getActiveTouchSlop(MotionEvent ev, int pointerIndex, PointF downPos);
 
-        abstract float getVelocity(VelocityTracker tracker);
+        abstract float getVelocity(VelocityTracker tracker, boolean isRtl);
+
+        abstract boolean isPositive(float displacement);
+
+        abstract boolean isNegative(float displacement);
     }
 
     public static final Direction VERTICAL = new Direction() {
 
         @Override
-        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
+        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
             return ev.getY(pointerIndex) - refPoint.y;
         }
 
@@ -87,16 +94,32 @@
         }
 
         @Override
-        float getVelocity(VelocityTracker tracker) {
+        float getVelocity(VelocityTracker tracker, boolean isRtl) {
             return tracker.getYVelocity();
         }
+
+        @Override
+        boolean isPositive(float displacement) {
+            // Up
+            return displacement < 0;
+        }
+
+        @Override
+        boolean isNegative(float displacement) {
+            // Down
+            return displacement > 0;
+        }
     };
 
     public static final Direction HORIZONTAL = new Direction() {
 
         @Override
-        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint) {
-            return ev.getX(pointerIndex) - refPoint.x;
+        float getDisplacement(MotionEvent ev, int pointerIndex, PointF refPoint, boolean isRtl) {
+            float displacement = ev.getX(pointerIndex) - refPoint.x;
+            if (isRtl) {
+                displacement = -displacement;
+            }
+            return displacement;
         }
 
         @Override
@@ -105,8 +128,24 @@
         }
 
         @Override
-        float getVelocity(VelocityTracker tracker) {
-            return tracker.getXVelocity();
+        float getVelocity(VelocityTracker tracker, boolean isRtl) {
+            float velocity = tracker.getXVelocity();
+            if (isRtl) {
+                velocity = -velocity;
+            }
+            return velocity;
+        }
+
+        @Override
+        boolean isPositive(float displacement) {
+            // Right
+            return displacement > 0;
+        }
+
+        @Override
+        boolean isNegative(float displacement) {
+            // Left
+            return displacement < 0;
         }
     };
 
@@ -159,6 +198,7 @@
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
     private final Direction mDir;
+    private final boolean mIsRtl;
 
     private final float mTouchSlop;
     private final float mMaxVelocity;
@@ -183,14 +223,15 @@
     }
 
     public SwipeDetector(@NonNull Context context, @NonNull Listener l, @NonNull Direction dir) {
-        this(ViewConfiguration.get(context), l, dir);
+        this(ViewConfiguration.get(context), l, dir, Utilities.isRtl(context.getResources()));
     }
 
     @VisibleForTesting
     protected SwipeDetector(@NonNull ViewConfiguration config, @NonNull Listener l,
-            @NonNull Direction dir) {
+            @NonNull Direction dir, boolean isRtl) {
         mListener = l;
         mDir = dir;
+        mIsRtl = isRtl;
         mTouchSlop = config.getScaledTouchSlop();
         mMaxVelocity = config.getScaledMaximumFlingVelocity();
     }
@@ -212,8 +253,8 @@
         }
 
         // Check if the client is interested in scroll in current direction.
-        if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDisplacement > 0) ||
-                ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDisplacement < 0)) {
+        if (((mScrollConditions & DIRECTION_NEGATIVE) > 0 && mDir.isNegative(mDisplacement)) ||
+                ((mScrollConditions & DIRECTION_POSITIVE) > 0 && mDir.isPositive(mDisplacement))) {
             return true;
         }
         return false;
@@ -259,7 +300,7 @@
                 if (pointerIndex == INVALID_POINTER_ID) {
                     break;
                 }
-                mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos);
+                mDisplacement = mDir.getDisplacement(ev, pointerIndex, mDownPos, mIsRtl);
 
                 // handle state and listener calls.
                 if (mState != ScrollState.DRAGGING && shouldScrollStart(ev, pointerIndex)) {
@@ -315,7 +356,7 @@
      * @see #DIRECTION_BOTH
      */
     public boolean wasInitialTouchPositive() {
-        return mSubtractDisplacement < 0;
+        return mDir.isPositive(mSubtractDisplacement);
     }
 
     private boolean reportDragging() {
@@ -332,7 +373,7 @@
 
     private void reportDragEnd() {
         mVelocityTracker.computeCurrentVelocity(1000, mMaxVelocity);
-        float velocity = mDir.getVelocity(mVelocityTracker) / 1000;
+        float velocity = mDir.getVelocity(mVelocityTracker, mIsRtl) / 1000;
         if (DBG) {
             Log.d(TAG, String.format("onScrollEnd disp=%.1f, velocity=%.1f",
                     mDisplacement, velocity));
diff --git a/src/com/android/launcher3/views/BaseDragLayer.java b/src/com/android/launcher3/views/BaseDragLayer.java
index 4545a1e..bd6bfd6 100644
--- a/src/com/android/launcher3/views/BaseDragLayer.java
+++ b/src/com/android/launcher3/views/BaseDragLayer.java
@@ -114,16 +114,28 @@
     }
 
     protected boolean findActiveController(MotionEvent ev) {
+        if (com.android.launcher3.TestProtocol.sDebugTracing) {
+            android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                    "mActiveController = null");
+        }
         mActiveController = null;
 
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mActivity);
         if (topView != null && topView.onControllerInterceptTouchEvent(ev)) {
+            if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                        "setting controller1: " + topView.getClass().getSimpleName());
+            }
             mActiveController = topView;
             return true;
         }
 
         for (TouchController controller : mControllers) {
             if (controller.onControllerInterceptTouchEvent(ev)) {
+                if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                    android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                            "setting controller1: " + controller.getClass().getSimpleName());
+                }
                 mActiveController = controller;
                 return true;
             }
@@ -193,8 +205,17 @@
         }
 
         if (mActiveController != null) {
+            if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                        "BaseDragLayer before onControllerTouchEvent " +
+                                mActiveController.getClass().getSimpleName());
+            }
             return mActiveController.onControllerTouchEvent(ev);
         } else {
+            if (com.android.launcher3.TestProtocol.sDebugTracing) {
+                android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
+                        "BaseDragLayer no controller");
+            }
             // In case no child view handled the touch event, we may not get onIntercept anymore
             return findActiveController(ev);
         }
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index 2a5418d..49ec292 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -19,6 +19,8 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.graphics.Canvas;
@@ -47,6 +49,7 @@
 import com.android.launcher3.Utilities;
 import com.android.launcher3.anim.Interpolators;
 import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
 import com.android.launcher3.folder.FolderIcon;
 import com.android.launcher3.folder.FolderShape;
 import com.android.launcher3.graphics.ShiftedBitmapDrawable;
@@ -157,11 +160,13 @@
 
     @Override
     public void onAnimationEnd(Animator animator) {
-        if (mRevealAnimator != null) {
-            mRevealAnimator.end();
-        }
         if (mEndRunnable != null) {
             mEndRunnable.run();
+        } else {
+            // End runnable also ends the reveal animator, so we manually handle it here.
+            if (mRevealAnimator != null) {
+                mRevealAnimator.end();
+            }
         }
     }
 
@@ -221,6 +226,7 @@
         new Handler(Looper.getMainLooper()).post(() -> {
             if (isAdaptiveIcon) {
                 mIsAdaptiveIcon = true;
+                boolean isFolderIcon = finalDrawable instanceof FolderAdaptiveIcon;
 
                 AdaptiveIconDrawable adaptiveIcon = (AdaptiveIconDrawable) finalDrawable;
                 Drawable background = adaptiveIcon.getBackground();
@@ -234,20 +240,27 @@
                 }
                 mForeground = foreground;
 
-                mFinalDrawableBounds.set(iconOffset, iconOffset, lp.width -
-                        iconOffset, mOriginalHeight - iconOffset);
                 if (mForeground instanceof ShiftedBitmapDrawable && v instanceof FolderIcon) {
                     ShiftedBitmapDrawable sbd = (ShiftedBitmapDrawable) mForeground;
                     ((FolderIcon) v).getPreviewBounds(sTmpRect);
                     sbd.setShiftX(sbd.getShiftX() - sTmpRect.left);
                     sbd.setShiftY(sbd.getShiftY() - sTmpRect.top);
                 }
+
+                int blurMargin = mBlurSizeOutline / 2;
+                mFinalDrawableBounds.set(0, 0, lp.width, mOriginalHeight);
+                if (!isFolderIcon) {
+                    mFinalDrawableBounds.inset(iconOffset - blurMargin, iconOffset - blurMargin);
+                }
                 mForeground.setBounds(mFinalDrawableBounds);
                 mBackground.setBounds(mFinalDrawableBounds);
 
-                int blurMargin = mBlurSizeOutline / 2;
-                mStartRevealRect.set(blurMargin, blurMargin , lp.width - blurMargin,
-                        mOriginalHeight - blurMargin);
+                if (isFolderIcon) {
+                    mStartRevealRect.set(0, 0, lp.width, mOriginalHeight);
+                } else {
+                    mStartRevealRect.set(mBlurSizeOutline, mBlurSizeOutline,
+                            lp.width - mBlurSizeOutline, mOriginalHeight - mBlurSizeOutline);
+                }
 
                 if (aspectRatio > 0) {
                     lp.height = (int) Math.max(lp.height, lp.width * aspectRatio);
@@ -380,12 +393,53 @@
                 originalView.setVisibility(INVISIBLE);
             }
         };
-        view.mEndRunnable = () -> {
-            ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view);
-            if (hideOriginal) {
-                originalView.setVisibility(VISIBLE);
-            }
-        };
+        if (hideOriginal) {
+            view.mEndRunnable = () -> {
+                AnimatorSet fade = new AnimatorSet();
+                fade.setDuration(200);
+                fade.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationStart(Animator animation) {
+                        originalView.setVisibility(VISIBLE);
+
+                        if (originalView instanceof FolderIcon) {
+                            FolderIcon folderIcon = (FolderIcon) originalView;
+                            folderIcon.setBackgroundVisible(false);
+                            folderIcon.getFolderName().setTextVisibility(false);
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        ((ViewGroup) dragLayer.getParent()).getOverlay().remove(view);
+
+                        if (view.mRevealAnimator != null) {
+                            view.mRevealAnimator.end();
+                        }
+                    }
+                });
+
+                if (originalView instanceof FolderIcon) {
+                    FolderIcon folderIcon = (FolderIcon) originalView;
+                    fade.play(folderIcon.getFolderName().createTextAlphaAnimator(true));
+                    fade.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            folderIcon.setBackgroundVisible(true);
+                            folderIcon.animateBgShadowAndStroke();
+                            if (folderIcon.hasDot()) {
+                                folderIcon.animateDotScale(0, 1f);
+                            }
+                        }
+                    });
+                } else {
+                    fade.play(ObjectAnimator.ofFloat(originalView, ALPHA, 0f, 1f));
+                }
+                fade.start();
+                // TODO: Do not run fade animation until we fix b/129421279.
+                fade.end();
+            };
+        }
         return view;
     }
 }
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index e259cfe..6a2f0ff 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -217,7 +217,7 @@
         if (!TextUtils.isEmpty(pickerPackage)) {
             intent.setPackage(pickerPackage);
         }
-        return launcher.startActivitySafely(v, intent, null);
+        return launcher.startActivitySafely(v, intent, null, null);
     }
 
     public static class OptionItem {
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java b/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.java
deleted file mode 100644
index 9133b07..0000000
--- a/src_ui_overrides/com/android/launcher3/uioverrides/BackgroundAppState.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 background app state
- */
-public class BackgroundAppState extends OverviewState {
-
-    public BackgroundAppState(int id) {
-        super(id);
-    }
-}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
similarity index 92%
rename from src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
rename to src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
index f7bb254..7006d77 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/AllAppsState.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
 import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
@@ -66,9 +66,9 @@
     }
 
     @Override
-    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
-        return new float[] { 1f, 0,
-                -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT};
+    public ScaleAndTranslation getWorkspaceScaleAndTranslation(Launcher launcher) {
+        return new ScaleAndTranslation(1f, 0,
+                -launcher.getAllAppsController().getShiftRange() * PARALLAX_COEFFICIENT);
     }
 
     @Override
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
similarity index 74%
rename from src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
rename to src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index 8def0d3..aeba788 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.launcher3.uioverrides;
+package com.android.launcher3.uioverrides.states;
 
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 
@@ -28,4 +28,16 @@
     public OverviewState(int id) {
         super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, FLAG_DISABLE_RESTORE);
     }
+
+    public static OverviewState newBackgroundState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newPeekState(int id) {
+        return new OverviewState(id);
+    }
+
+    public static OverviewState newSwitchState(int id) {
+        return new OverviewState(id);
+    }
 }
diff --git a/tests/Android.mk b/tests/Android.mk
index b9b703d..ca7395a 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -35,8 +35,8 @@
         ../src/com/android/launcher3/TestProtocol.java
 endif
 
-LOCAL_SDK_VERSION := current
 LOCAL_MODULE := ub-launcher-aosp-tapl
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
@@ -47,18 +47,18 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_STATIC_JAVA_LIBRARIES := \
-	androidx.test.runner \
-	androidx.test.rules \
-	androidx.test.uiautomator_uiautomator \
-	mockito-target-minus-junit4
+    androidx.test.runner \
+    androidx.test.rules \
+    androidx.test.uiautomator_uiautomator \
+    mockito-target-minus-junit4
 
 ifneq (,$(wildcard frameworks/base))
-  LOCAL_PRIVATE_PLATFORM_APIS := true
-  LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
+    LOCAL_PRIVATE_PLATFORM_APIS := true
+    LOCAL_STATIC_JAVA_LIBRARIES += launcher-aosp-tapl
 else
-  LOCAL_SDK_VERSION := 28
-  LOCAL_MIN_SDK_VERSION := 21
-  LOCAL_STATIC_JAVA_LIBRARIES += ub-launcher-aosp-tapl
+    LOCAL_SDK_VERSION := 28
+    LOCAL_MIN_SDK_VERSION := 21
+    LOCAL_STATIC_JAVA_LIBRARIES += ub-launcher-aosp-tapl
 endif
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
index b600473..4ebf54c 100644
--- a/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
+++ b/tests/src/com/android/launcher3/touch/SwipeDetectorTest.java
@@ -15,9 +15,12 @@
  */
 package com.android.launcher3.touch;
 
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyFloat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
 import android.util.Log;
 import android.view.ViewConfiguration;
 
@@ -29,11 +32,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyFloat;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -63,7 +64,7 @@
         doReturn(orgConfig.getScaledMaximumFlingVelocity()).when(mMockConfig)
                 .getScaledMaximumFlingVelocity();
 
-        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL);
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
         mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
         mTouchSlop = orgConfig.getScaledTouchSlop();
         doReturn(mTouchSlop).when(mMockConfig).getScaledTouchSlop();
@@ -72,7 +73,19 @@
     }
 
     @Test
-    public void testDragStart_vertical() {
+    public void testDragStart_verticalPositive() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100, 100 - mTouchSlop);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_verticalNegative() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.VERTICAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100, 100 + mTouchSlop);
         // TODO: actually calculate the following parameters and do exact value checks.
@@ -88,9 +101,42 @@
     }
 
     @Test
-    public void testDragStart_horizontal() {
-        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL);
-        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_BOTH, false);
+    public void testDragStart_horizontalPositive() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100 + mTouchSlop, 100);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_horizontalNegative() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, false);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
+
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100 - mTouchSlop, 100);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_horizontalRtlPositive() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
+
+        mGenerator.put(0, 100, 100);
+        mGenerator.move(0, 100 - mTouchSlop, 100);
+        // TODO: actually calculate the following parameters and do exact value checks.
+        verify(mMockListener).onDragStart(anyBoolean());
+    }
+
+    @Test
+    public void testDragStart_horizontalRtlNegative() {
+        mDetector = new SwipeDetector(mMockConfig, mMockListener, SwipeDetector.HORIZONTAL, true);
+        mDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_NEGATIVE, false);
 
         mGenerator.put(0, 100, 100);
         mGenerator.move(0, 100 + mTouchSlop, 100);
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 6f2f280..91ebd9b 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -34,6 +34,7 @@
 
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
+import com.android.launcher3.TestProtocol;
 import com.android.launcher3.popup.ArrowPopup;
 import com.android.launcher3.tapl.AllApps;
 import com.android.launcher3.tapl.AppIcon;
@@ -327,18 +328,23 @@
     @Test
     @PortraitLandscape
     public void testDragAppIcon() throws Throwable {
-        LauncherActivityInfo settingsApp = getSettingsApp();
+        try {
+            TestProtocol.sDebugTracing = true;
+            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());
+            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());
+        } finally {
+            TestProtocol.sDebugTracing = false;
+        }
     }
 
     @Test
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index fbb4f51..68b16d6 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -145,7 +145,7 @@
         assertFalse(mDevice.findObject(new UiSelector().description(info.label)).exists());
     }
 
-    @Test
+    @Test @Ignore
     public void testPendingWidget_autoRestored() {
         // A non-restored widget with no config screen gets restored automatically.
         LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 122151e..e4dced5 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -29,7 +29,7 @@
 public class AllApps extends LauncherInstrumentation.VisibleContainer {
     private static final int MAX_SCROLL_ATTEMPTS = 40;
     private static final int MIN_INTERACT_SIZE = 100;
-    private static final int FLING_SPEED = 3000;
+    private static final int FLING_SPEED = LauncherInstrumentation.needSlowGestures() ? 1000 : 3000;
 
     private final int mHeight;
 
@@ -60,47 +60,61 @@
      */
     @NonNull
     public AppIcon getAppIcon(String appName) {
-        final UiObject2 allAppsContainer = verifyActiveContainer();
-        final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
-        if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
-            scrollBackToBeginning();
-            int attempts = 0;
-            while (!hasClickableIcon(allAppsContainer, appIconSelector) &&
-                    allAppsContainer.scroll(Direction.DOWN, 0.8f)) {
-                LauncherInstrumentation.assertTrue(
-                        "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
-                        ++attempts <= MAX_SCROLL_ATTEMPTS);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get app icon on all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
+            allAppsContainer.setGestureMargins(0, 0, 0, navBar.getVisibleBounds().height());
+            final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
+            if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
+                scrollBackToBeginning();
+                int attempts = 0;
+                try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled")) {
+                    while (!hasClickableIcon(allAppsContainer, appIconSelector) &&
+                            allAppsContainer.scroll(Direction.DOWN, 0.8f)) {
+                        mLauncher.assertTrue(
+                                "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
+                                ++attempts <= MAX_SCROLL_ATTEMPTS);
+                        verifyActiveContainer();
+                    }
+                }
                 verifyActiveContainer();
             }
-        }
-        verifyActiveContainer();
 
-        final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer, appIconSelector);
-        ensureIconVisible(appIcon, allAppsContainer);
-        return new AppIcon(mLauncher, appIcon);
+            final UiObject2 appIcon = mLauncher.getObjectInContainer(allAppsContainer,
+                    appIconSelector);
+            ensureIconVisible(appIcon, allAppsContainer);
+            return new AppIcon(mLauncher, appIcon);
+        }
     }
 
     private void scrollBackToBeginning() {
-        final UiObject2 allAppsContainer = verifyActiveContainer();
-        final UiObject2 searchBox =
-                mLauncher.waitForObjectInContainer(allAppsContainer, "search_container_all_apps");
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to scroll back in all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            final UiObject2 searchBox =
+                    mLauncher.waitForObjectInContainer(allAppsContainer,
+                            "search_container_all_apps");
 
-        int attempts = 0;
-        allAppsContainer.setGestureMargins(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
+            int attempts = 0;
+            allAppsContainer.setGestureMargins(0, searchBox.getVisibleBounds().bottom + 1, 0, 5);
 
-        for (int scroll = getScroll(allAppsContainer);
-                scroll != 0;
-                scroll = getScroll(allAppsContainer)) {
-            LauncherInstrumentation.assertTrue("Negative scroll position", scroll > 0);
+            for (int scroll = getScroll(allAppsContainer);
+                    scroll != 0;
+                    scroll = getScroll(allAppsContainer)) {
+                mLauncher.assertTrue("Negative scroll position", scroll > 0);
 
-            LauncherInstrumentation.assertTrue(
-                    "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
-                    ++attempts <= MAX_SCROLL_ATTEMPTS);
+                mLauncher.assertTrue(
+                        "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
+                        ++attempts <= MAX_SCROLL_ATTEMPTS);
 
-            allAppsContainer.scroll(Direction.UP, 1);
+                allAppsContainer.scroll(Direction.UP, 1);
+            }
+
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("scrolled up")) {
+                verifyActiveContainer();
+            }
         }
-
-        verifyActiveContainer();
     }
 
     private int getScroll(UiObject2 allAppsContainer) {
@@ -115,8 +129,11 @@
             // to reveal the app icon to have the MIN_INTERACT_SIZE
             final float pct = Math.max(((float) (MIN_INTERACT_SIZE - appHeight)) / mHeight, 0.2f);
             allAppsContainer.scroll(Direction.DOWN, pct);
-            mLauncher.waitForIdle();
-            verifyActiveContainer();
+            try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                    "scrolled an icon in all apps to make it visible - and then")) {
+                mLauncher.waitForIdle();
+                verifyActiveContainer();
+            }
         }
     }
 
@@ -124,21 +141,29 @@
      * Flings forward (down) and waits the fling's end.
      */
     public void flingForward() {
-        final UiObject2 allAppsContainer = verifyActiveContainer();
-        // Start the gesture in the center to avoid starting at elements near the top.
-        allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
-        allAppsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        verifyActiveContainer();
+        try(LauncherInstrumentation.Closable c =
+                    mLauncher.addContextLayer("want to fling forward in all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            // Start the gesture in the center to avoid starting at elements near the top.
+            allAppsContainer.setGestureMargins(0, 0, 0, mHeight / 2);
+            allAppsContainer.fling(Direction.DOWN,
+                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            verifyActiveContainer();
+        }
     }
 
     /**
      * Flings backward (up) and waits the fling's end.
      */
     public void flingBackward() {
-        final UiObject2 allAppsContainer = verifyActiveContainer();
-        // Start the gesture in the center, for symmetry with forward.
-        allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
-        allAppsContainer.fling(Direction.UP, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        verifyActiveContainer();
+        try(LauncherInstrumentation.Closable c =
+                    mLauncher.addContextLayer("want to fling backward in all apps")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            // Start the gesture in the center, for symmetry with forward.
+            allAppsContainer.setGestureMargins(0, mHeight / 2, 0, 0);
+            allAppsContainer.fling(Direction.UP,
+                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            verifyActiveContainer();
+        }
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
index dcc51b5..c3b671b 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
@@ -42,20 +42,24 @@
      */
     @NonNull
     public Overview switchBackToOverview() {
-        final UiObject2 allAppsContainer = verifyActiveContainer();
-        // Swipe from the search box to the bottom.
-        final UiObject2 qsb = mLauncher.waitForObjectInContainer(
-                allAppsContainer, "search_container_all_apps");
-        final Point start = qsb.getVisibleCenter();
-        final int swipeHeight = mLauncher.getTestInfo(
-                TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT).
-                getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch back from all apps to overview")) {
+            final UiObject2 allAppsContainer = verifyActiveContainer();
+            // Swipe from the search box to the bottom.
+            final UiObject2 qsb = mLauncher.waitForObjectInContainer(
+                    allAppsContainer, "search_container_all_apps");
+            final Point start = qsb.getVisibleCenter();
+            final int swipeHeight = mLauncher.getTestInfo(
+                    TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT).
+                    getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
 
-        final int endY = start.y + swipeHeight + mLauncher.getTouchSlop();
-        LauncherInstrumentation.log("AllAppsFromOverview.switchBackToOverview before swipe");
-        mLauncher.swipe(start.x, start.y, start.x, endY, OVERVIEW_STATE_ORDINAL);
+            final int endY = start.y + swipeHeight + mLauncher.getTouchSlop();
+            LauncherInstrumentation.log("AllAppsFromOverview.switchBackToOverview before swipe");
+            mLauncher.swipe(start.x, start.y, start.x, endY, OVERVIEW_STATE_ORDINAL);
 
-        return new Overview(mLauncher);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("swiped down")) {
+                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 ef509ed..358d5e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -20,8 +20,6 @@
 import static com.android.launcher3.tapl.LauncherInstrumentation.WAIT_TIME_MS;
 import static com.android.launcher3.tapl.TestHelpers.getOverviewPackageName;
 
-import static org.junit.Assert.assertTrue;
-
 import android.graphics.Point;
 import android.os.SystemClock;
 import android.view.MotionEvent;
@@ -57,11 +55,14 @@
      */
     @NonNull
     public BaseOverview switchToOverview() {
-        verifyActiveContainer();
-        goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
-        assertTrue("Overview not visible", mLauncher.getDevice().wait(
-                Until.hasObject(By.pkg(getOverviewPackageName())), WAIT_TIME_MS));
-        return new BaseOverview(mLauncher);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch from background to overview")) {
+            verifyActiveContainer();
+            goToOverviewUnchecked(BACKGROUND_APP_STATE_ORDINAL);
+            mLauncher.assertTrue("Overview not visible", mLauncher.getDevice().wait(
+                    Until.hasObject(By.pkg(getOverviewPackageName())), WAIT_TIME_MS));
+            return new BaseOverview(mLauncher);
+        }
     }
 
     protected void goToOverviewUnchecked(int expectedState) {
diff --git a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
index 234c3bf..4205188 100644
--- a/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/BaseOverview.java
@@ -44,42 +44,54 @@
      * Flings forward (left) and waits the fling's end.
      */
     public void flingForward() {
-        LauncherInstrumentation.log("Overview.flingForward before fling");
-        final UiObject2 overview = verifyActiveContainer();
-        final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
-        overview.setGestureMargins(margin, 0, 0, 0);
-        overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        mLauncher.waitForIdle();
-        verifyActiveContainer();
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to fling forward in overview")) {
+            LauncherInstrumentation.log("Overview.flingForward before fling");
+            final UiObject2 overview = verifyActiveContainer();
+            final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+            overview.setGestureMargins(margin, 0, 0, 0);
+            overview.fling(Direction.LEFT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+            verifyActiveContainer();
+        }
     }
 
     /**
      * Dismissed all tasks by scrolling to Clear-all button and pressing it.
      */
     public Workspace dismissAllTasks() {
-        final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all");
-        for (int i = 0;
-                i < FLINGS_FOR_DISMISS_LIMIT
-                        && !verifyActiveContainer().hasObject(clearAllSelector);
-                ++i) {
-            flingForward();
-        }
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "dismissing all tasks")) {
+            final BySelector clearAllSelector = mLauncher.getLauncherObjectSelector("clear_all");
+            for (int i = 0;
+                    i < FLINGS_FOR_DISMISS_LIMIT
+                            && !verifyActiveContainer().hasObject(clearAllSelector);
+                    ++i) {
+                flingForward();
+            }
 
-        mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
-        return new Workspace(mLauncher);
+            mLauncher.getObjectInContainer(verifyActiveContainer(), clearAllSelector).click();
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "dismissed all tasks")) {
+                return new Workspace(mLauncher);
+            }
+        }
     }
 
     /**
      * Flings backward (right) and waits the fling's end.
      */
     public void flingBackward() {
-        LauncherInstrumentation.log("Overview.flingBackward before fling");
-        final UiObject2 overview = verifyActiveContainer();
-        final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
-        overview.setGestureMargins(0, 0, margin, 0);
-        overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        mLauncher.waitForIdle();
-        verifyActiveContainer();
+        try (LauncherInstrumentation.Closable c =
+                     mLauncher.addContextLayer("want to fling backward in overview")) {
+            LauncherInstrumentation.log("Overview.flingBackward before fling");
+            final UiObject2 overview = verifyActiveContainer();
+            final int margin = (int) (50 * mLauncher.getDisplayDensity()) + 1;
+            overview.setGestureMargins(0, 0, margin, 0);
+            overview.fling(Direction.RIGHT, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+            verifyActiveContainer();
+        }
     }
 
     /**
@@ -89,18 +101,21 @@
      */
     @NonNull
     public OverviewTask getCurrentTask() {
-        verifyActiveContainer();
-        final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
-                mLauncher.getLauncherObjectSelector("snapshot"));
-        LauncherInstrumentation.assertNotEquals("Unable to find a task", 0, taskViews.size());
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get current task")) {
+            verifyActiveContainer();
+            final List<UiObject2> taskViews = mLauncher.getDevice().findObjects(
+                    mLauncher.getLauncherObjectSelector("snapshot"));
+            mLauncher.assertNotEquals("Unable to find a task", 0, taskViews.size());
 
-        // taskViews contains up to 3 task views: the 'main' (having the widest visible
-        // part) one in the center, and parts of its right and left siblings. Find the
-        // main task view by its width.
-        final UiObject2 widestTask = Collections.max(taskViews,
-                (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
-                        t2.getVisibleBounds().width()));
+            // taskViews contains up to 3 task views: the 'main' (having the widest visible
+            // part) one in the center, and parts of its right and left siblings. Find the
+            // main task view by its width.
+            final UiObject2 widestTask = Collections.max(taskViews,
+                    (t1, t2) -> Integer.compare(t1.getVisibleBounds().width(),
+                            t2.getVisibleBounds().width()));
 
-        return new OverviewTask(mLauncher, widestTask, this);
+            return new OverviewTask(mLauncher, widestTask, this);
+        }
     }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Home.java b/tests/tapl/com/android/launcher3/tapl/Home.java
index f8bd85a..20c116c 100644
--- a/tests/tapl/com/android/launcher3/tapl/Home.java
+++ b/tests/tapl/com/android/launcher3/tapl/Home.java
@@ -48,8 +48,14 @@
     @NonNull
     @Override
     public Overview switchToOverview() {
-        verifyActiveContainer();
-        goToOverviewUnchecked(OVERVIEW_STATE_ORDINAL);
-        return new Overview(mLauncher);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch from home to overview")) {
+            verifyActiveContainer();
+            goToOverviewUnchecked(OVERVIEW_STATE_ORDINAL);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "performed the switch action")) {
+                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 481281a..7a2b7af 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -59,10 +59,10 @@
     private Background launch(String errorMessage, BySelector selector) {
         LauncherInstrumentation.log("Launchable.launch before click " +
                 mObject.getVisibleCenter());
-        LauncherInstrumentation.assertTrue(
+        mLauncher.assertTrue(
                 "Launching an app didn't open a new window: " + mObject.getText(),
                 mObject.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
-        LauncherInstrumentation.assertTrue(
+        mLauncher.assertTrue(
                 "App didn't start: " + errorMessage,
                 mLauncher.getDevice().wait(Until.hasObject(selector),
                         LauncherInstrumentation.WAIT_TIME_MS));
@@ -79,6 +79,9 @@
                 this,
                 new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2),
                 DRAG_SPEED);
-        return new Workspace(mLauncher);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "dragged launchable to workspace")) {
+            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 f44022b..b71f790 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -17,19 +17,19 @@
 package com.android.launcher3.tapl;
 
 import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
-import static com.android.systemui.shared.system.SettingsCompat.SWIPE_UP_SETTING_NAME;
 
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.UiAutomation;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.graphics.Point;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.provider.Settings;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.MotionEvent;
@@ -47,12 +47,14 @@
 import androidx.test.uiautomator.Until;
 
 import com.android.launcher3.TestProtocol;
-import com.android.quickstep.SwipeUpSetting;
+import com.android.systemui.shared.system.QuickStepContract;
 
 import org.junit.Assert;
 
 import java.io.IOException;
 import java.lang.ref.WeakReference;
+import java.util.Deque;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
 
@@ -90,11 +92,16 @@
          * @return UI object for the container.
          */
         final UiObject2 verifyActiveContainer() {
-            assertTrue("Attempt to use a stale container", this == sActiveContainer.get());
+            mLauncher.assertTrue("Attempt to use a stale container",
+                    this == sActiveContainer.get());
             return mLauncher.verifyContainerType(getContainerType());
         }
     }
 
+    interface Closable extends AutoCloseable {
+        void close();
+    }
+
     private static final String WORKSPACE_RES_ID = "workspace";
     private static final String APPS_RES_ID = "apps_view";
     private static final String OVERVIEW_RES_ID = "overview_panel";
@@ -108,6 +115,7 @@
     private final Instrumentation mInstrumentation;
     private int mExpectedRotation = Surface.ROTATION_0;
     private final Uri mTestProviderUri;
+    private final Deque<String> mDiagnosticContext = new LinkedList<>();
 
     /**
      * Constructs the root of TAPL hierarchy. You get all other objects from it.
@@ -158,48 +166,66 @@
     }
 
     public NavigationModel getNavigationModel() {
-        return isSwipeUpEnabled() ? NavigationModel.TWO_BUTTON : NavigationModel.THREE_BUTTON;
+        final Context baseContext = mInstrumentation.getTargetContext();
+        try {
+            // Workaround, use constructed context because both the instrumentation context and the
+            // app context are not constructed with resources that take overlays into account
+            final Context ctx = baseContext.createPackageContext("android", 0);
+            if (QuickStepContract.isGesturalMode(ctx)) {
+                return NavigationModel.ZERO_BUTTON;
+            } else if (QuickStepContract.isSwipeUpMode(ctx)) {
+                return NavigationModel.TWO_BUTTON;
+            } else if (QuickStepContract.isLegacyMode(ctx)) {
+                return NavigationModel.THREE_BUTTON;
+            } else {
+                fail("Can't detect navigation mode");
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            fail(e.toString());
+        }
+        return NavigationModel.THREE_BUTTON;
     }
 
-    private boolean isSwipeUpEnabled() {
-        final boolean swipeUpEnabledDefaultValue = SwipeUpSetting.isSwipeUpEnabledDefaultValue();
-        return SwipeUpSetting.isSwipeUpSettingAvailable() ?
-                Settings.Secure.getInt(
-                        mInstrumentation.getTargetContext().getContentResolver(),
-                        SWIPE_UP_SETTING_NAME,
-                        swipeUpEnabledDefaultValue ? 1 : 0) == 1 :
-                swipeUpEnabledDefaultValue;
+    static boolean needSlowGestures() {
+        return Build.MODEL.contains("Cuttlefish");
     }
 
     static void log(String message) {
         Log.d(TAG, message);
     }
 
-    private static void fail(String message) {
-        Assert.fail("http://go/tapl : " + message);
+    Closable addContextLayer(String piece) {
+        mDiagnosticContext.addLast(piece);
+        return () -> mDiagnosticContext.removeLast();
     }
 
-    static void assertTrue(String message, boolean condition) {
+    private void fail(String message) {
+        final String ctxt = mDiagnosticContext.isEmpty() ? "" : String.join(", ",
+                mDiagnosticContext) + "; ";
+        Assert.fail("http://go/tapl : " + ctxt + message);
+    }
+
+    void assertTrue(String message, boolean condition) {
         if (!condition) {
             fail(message);
         }
     }
 
-    static void assertNotNull(String message, Object object) {
+    void assertNotNull(String message, Object object) {
         assertTrue(message, object != null);
     }
 
-    static private void failEquals(String message, Object actual) {
+    private void failEquals(String message, Object actual) {
         fail(message + ". " + "Actual: " + actual);
     }
 
-    static private void assertEquals(String message, int expected, int actual) {
+    private void assertEquals(String message, int expected, int actual) {
         if (expected != actual) {
             fail(message + " expected: " + expected + " but was: " + actual);
         }
     }
 
-    static void assertNotEquals(String message, int unexpected, int actual) {
+    void assertNotEquals(String message, int unexpected, int actual) {
         if (unexpected == actual) {
             failEquals(message, actual);
         }
@@ -212,54 +238,61 @@
     private UiObject2 verifyContainerType(ContainerType containerType) {
         assertEquals("Unexpected display rotation",
                 mExpectedRotation, mDevice.getDisplayRotation());
-        assertTrue("Presence of recents button doesn't match isSwipeUpEnabled()",
-                isSwipeUpEnabled() ==
-                        (mDevice.findObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")) == null));
+        final NavigationModel navigationModel = getNavigationModel();
+        assertTrue("Presence of recents button doesn't match the interaction mode",
+                (navigationModel == NavigationModel.THREE_BUTTON) ==
+                        mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, "recent_apps")));
+        assertTrue("Presence of home button doesn't match the interaction mode",
+                (navigationModel != NavigationModel.ZERO_BUTTON) ==
+                        mDevice.hasObject(By.res(SYSTEMUI_PACKAGE, "home")));
         log("verifyContainerType: " + containerType);
 
-        switch (containerType) {
-            case WORKSPACE: {
-                waitForLauncherObject(APPS_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return waitForLauncherObject(WORKSPACE_RES_ID);
-            }
-            case WIDGETS: {
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(APPS_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                return waitForLauncherObject(WIDGETS_RES_ID);
-            }
-            case ALL_APPS: {
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return waitForLauncherObject(APPS_RES_ID);
-            }
-            case OVERVIEW: {
-                if (mDevice.isNaturalOrientation()) {
+        try (Closable c = addContextLayer(
+                "but the current state is not " + containerType.name())) {
+            switch (containerType) {
+                case WORKSPACE: {
                     waitForLauncherObject(APPS_RES_ID);
-                } else {
-                    waitUntilGone(APPS_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+                    return waitForLauncherObject(WORKSPACE_RES_ID);
                 }
-                // Fall through
-            }
-            case BASE_OVERVIEW: {
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
+                case WIDGETS: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(APPS_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    return waitForLauncherObject(WIDGETS_RES_ID);
+                }
+                case ALL_APPS: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+                    return waitForLauncherObject(APPS_RES_ID);
+                }
+                case OVERVIEW: {
+                    if (mDevice.isNaturalOrientation()) {
+                        waitForLauncherObject(APPS_RES_ID);
+                    } else {
+                        waitUntilGone(APPS_RES_ID);
+                    }
+                    // Fall through
+                }
+                case BASE_OVERVIEW: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
 
-                return waitForLauncherObject(OVERVIEW_RES_ID);
+                    return waitForLauncherObject(OVERVIEW_RES_ID);
+                }
+                case BACKGROUND: {
+                    waitUntilGone(WORKSPACE_RES_ID);
+                    waitUntilGone(APPS_RES_ID);
+                    waitUntilGone(OVERVIEW_RES_ID);
+                    waitUntilGone(WIDGETS_RES_ID);
+                    return null;
+                }
+                default:
+                    fail("Invalid state: " + containerType);
+                    return null;
             }
-            case BACKGROUND: {
-                waitUntilGone(WORKSPACE_RES_ID);
-                waitUntilGone(APPS_RES_ID);
-                waitUntilGone(OVERVIEW_RES_ID);
-                waitUntilGone(WIDGETS_RES_ID);
-                return null;
-            }
-            default:
-                fail("Invalid state: " + containerType);
-                return null;
         }
     }
 
@@ -297,20 +330,21 @@
         // We need waiting for any accessibility event generated after pressing Home because
         // otherwise waitForIdle may return immediately in case when there was a big enough pause in
         // accessibility events prior to pressing Home.
+        final String action;
         if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
             if (hasLauncherObject(WORKSPACE_RES_ID)) {
-                log("0-button pressHome: already in workspace");
+                log(action = "0-button: already in workspace");
             } else if (hasLauncherObject(OVERVIEW_RES_ID)) {
-                log("0-button pressHome: overview");
+                log(action = "0-button: from overview");
                 mDevice.pressHome();
             } else if (hasLauncherObject(WIDGETS_RES_ID)) {
-                log("0-button pressHome: widgets");
+                log(action = "0-button: from widgets");
                 mDevice.pressHome();
             } else if (hasLauncherObject(APPS_RES_ID)) {
-                log("0-button pressHome: all apps");
+                log(action = "0-button: from all apps");
                 mDevice.pressHome();
             } else {
-                log("0-button pressHome: another app");
+                log(action = "0-button: from another app");
                 assertTrue("Launcher is visible, don't know how to go home",
                         !mDevice.hasObject(By.pkg(getLauncherPackageName())));
                 final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
@@ -321,6 +355,7 @@
                         BACKGROUND_APP_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
             }
         } else {
+            log(action = "clicking home button");
             executeAndWaitForEvent(
                     () -> {
                         log("LauncherInstrumentation.pressHome before clicking");
@@ -340,7 +375,10 @@
                     "Pressing Home didn't produce any events");
             mDevice.waitForIdle();
         }
-        return getWorkspace();
+        try (LauncherInstrumentation.Closable c = addContextLayer(
+                "performed action to switch to Home - " + action)) {
+            return getWorkspace();
+        }
     }
 
     /**
@@ -351,7 +389,9 @@
      */
     @NonNull
     public Workspace getWorkspace() {
-        return new Workspace(this);
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get workspace object")) {
+            return new Workspace(this);
+        }
     }
 
     /**
@@ -373,7 +413,9 @@
      */
     @NonNull
     public Widgets getAllWidgets() {
-        return new Widgets(this);
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get widgets")) {
+            return new Widgets(this);
+        }
     }
 
     /**
@@ -384,7 +426,9 @@
      */
     @NonNull
     public Overview getOverview() {
-        return new Overview(this);
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get overview")) {
+            return new Overview(this);
+        }
     }
 
     /**
@@ -408,7 +452,9 @@
      */
     @NonNull
     public AllApps getAllApps() {
-        return new AllApps(this);
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get all apps object")) {
+            return new AllApps(this);
+        }
     }
 
     /**
@@ -421,7 +467,9 @@
      */
     @NonNull
     public AllAppsFromOverview getAllAppsFromOverview() {
-        return new AllAppsFromOverview(this);
+        try (LauncherInstrumentation.Closable c = addContextLayer("want to get all apps object")) {
+            return new AllAppsFromOverview(this);
+        }
     }
 
     void waitUntilGone(String resId) {
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 5c8d5eb..41932c8 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -20,11 +20,11 @@
 
 import android.graphics.Point;
 
-import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
-
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.tapl.LauncherInstrumentation.ContainerType;
+
 /**
  * Overview pane.
  */
@@ -47,14 +47,20 @@
      */
     @NonNull
     public AllAppsFromOverview switchToAllApps() {
-        verifyActiveContainer();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to switch from overview to all apps")) {
+            verifyActiveContainer();
 
-        // Swipe from navbar to the top.
-        final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
-        final Point start = navBar.getVisibleCenter();
-        LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
-        mLauncher.swipe(start.x, start.y, start.x, 0, ALL_APPS_STATE_ORDINAL);
+            // Swipe from navbar to the top.
+            final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
+            final Point start = navBar.getVisibleCenter();
+            LauncherInstrumentation.log("Overview.switchToAllApps before swipe");
+            mLauncher.swipe(start.x, start.y, start.x, 0, ALL_APPS_STATE_ORDINAL);
 
-        return new AllAppsFromOverview(mLauncher);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "swiped all way up from overview")) {
+                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 7ccd49b..b966851 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -44,10 +44,13 @@
      * Swipes the task up.
      */
     public void dismiss() {
-        verifyActiveContainer();
-        // Dismiss the task via flinging it up.
-        mTask.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        mLauncher.waitForIdle();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to dismiss a task")) {
+            verifyActiveContainer();
+            // Dismiss the task via flinging it up.
+            mTask.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+        }
     }
 
     /**
@@ -55,7 +58,7 @@
      */
     public Background open() {
         verifyActiveContainer();
-        LauncherInstrumentation.assertTrue("Launching task didn't open a new window: " +
+        mLauncher.assertTrue("Launching task didn't open a new window: " +
                         mTask.getParent().getContentDescription(),
                 mTask.clickAndWait(Until.newWindow(), LauncherInstrumentation.WAIT_TIME_MS));
         return new Background(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/Widgets.java b/tests/tapl/com/android/launcher3/tapl/Widgets.java
index 89affd1..2de53c3 100644
--- a/tests/tapl/com/android/launcher3/tapl/Widgets.java
+++ b/tests/tapl/com/android/launcher3/tapl/Widgets.java
@@ -34,21 +34,37 @@
      * Flings forward (down) and waits the fling's end.
      */
     public void flingForward() {
-        final UiObject2 widgetsContainer = verifyActiveContainer();
-        widgetsContainer.setGestureMargin(100);
-        widgetsContainer.fling(Direction.DOWN, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        verifyActiveContainer();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to fling forward in widgets")) {
+            LauncherInstrumentation.log("Widgets.flingForward enter");
+            final UiObject2 widgetsContainer = verifyActiveContainer();
+            widgetsContainer.setGestureMargin(100);
+            widgetsContainer.fling(Direction.DOWN,
+                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung forward")) {
+                verifyActiveContainer();
+            }
+            LauncherInstrumentation.log("Widgets.flingForward exit");
+        }
     }
 
     /**
      * Flings backward (up) and waits the fling's end.
      */
     public void flingBackward() {
-        final UiObject2 widgetsContainer = verifyActiveContainer();
-        widgetsContainer.setGestureMargin(100);
-        widgetsContainer.fling(Direction.UP, (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
-        mLauncher.waitForIdle();
-        verifyActiveContainer();
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to fling backwards in widgets")) {
+            LauncherInstrumentation.log("Widgets.flingBackward enter");
+            final UiObject2 widgetsContainer = verifyActiveContainer();
+            widgetsContainer.setGestureMargin(100);
+            widgetsContainer.fling(Direction.UP,
+                    (int) (FLING_SPEED * mLauncher.getDisplayDensity()));
+            mLauncher.waitForIdle();
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
+                verifyActiveContainer();
+            }
+            LauncherInstrumentation.log("Widgets.flingBackward exit");
+        }
     }
 
     @Override
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 7d97acd..22b5564 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -36,7 +36,7 @@
 public final class Workspace extends Home {
     private static final float FLING_SPEED = 3500.0F;
     private final UiObject2 mHotseat;
-    private final int ICON_DRAG_SPEED = 2000;
+    private final int ICON_DRAG_SPEED = LauncherInstrumentation.needSlowGestures() ? 100 : 570;
 
     Workspace(LauncherInstrumentation launcher) {
         super(launcher);
@@ -50,21 +50,27 @@
      */
     @NonNull
     public AllApps switchToAllApps() {
-        verifyActiveContainer();
-        final UiObject2 hotseat = mHotseat;
-        final Point start = hotseat.getVisibleCenter();
-        final int swipeHeight = mLauncher.getTestInfo(
-                TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
-                getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
-        mLauncher.swipe(
-                start.x,
-                start.y,
-                start.x,
-                start.y - swipeHeight - mLauncher.getTouchSlop(),
-                ALL_APPS_STATE_ORDINAL
-        );
+        try(LauncherInstrumentation.Closable c =
+                    mLauncher.addContextLayer("want to switch from workspace to all apps")) {
+            verifyActiveContainer();
+            final UiObject2 hotseat = mHotseat;
+            final Point start = hotseat.getVisibleCenter();
+            final int swipeHeight = mLauncher.getTestInfo(
+                    TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
+                    getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+            mLauncher.swipe(
+                    start.x,
+                    start.y,
+                    start.x,
+                    start.y - swipeHeight - mLauncher.getTouchSlop(),
+                    ALL_APPS_STATE_ORDINAL
+            );
 
-        return new AllApps(mLauncher);
+            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
+                    "swiped to all apps")) {
+                return new AllApps(mLauncher);
+            }
+        }
     }
 
     /**
@@ -75,9 +81,13 @@
      */
     @Nullable
     public AppIcon tryGetWorkspaceAppIcon(String appName) {
-        final UiObject2 workspace = verifyActiveContainer();
-        final UiObject2 icon = workspace.findObject(AppIcon.getAppIconSelector(appName, mLauncher));
-        return icon != null ? new AppIcon(mLauncher, icon) : null;
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+                "want to get a workspace icon")) {
+            final UiObject2 workspace = verifyActiveContainer();
+            final UiObject2 icon = workspace.findObject(
+                    AppIcon.getAppIconSelector(appName, mLauncher));
+            return icon != null ? new AppIcon(mLauncher, icon) : null;
+        }
     }
 
 
@@ -107,7 +117,7 @@
                     getHotseatAppIcon("Messages"),
                     new Point(mLauncher.getDevice().getDisplayWidth(),
                             workspace.getVisibleBounds().centerY()),
-                    ICON_DRAG_SPEED);
+                    (int) (ICON_DRAG_SPEED * mLauncher.getDisplayDensity()));
             verifyActiveContainer();
         }
         assertTrue("Home screen workspace didn't become scrollable",
@@ -126,7 +136,9 @@
 
     static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
             Point dest, int icon_drag_speed) {
+        LauncherInstrumentation.log("dragIconToWorkspace: begin");
         launchable.getObject().drag(dest, icon_drag_speed);
+        LauncherInstrumentation.log("dragIconToWorkspace: end");
         launcher.waitUntilGone("drop_target_bar");
     }
 
@@ -165,7 +177,9 @@
     public Widgets openAllWidgets() {
         verifyActiveContainer();
         mLauncher.getDevice().pressKeyCode(KeyEvent.KEYCODE_W, KeyEvent.META_CTRL_ON);
-        return new Widgets(mLauncher);
+        try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer("pressed Ctrl+W")) {
+            return new Widgets(mLauncher);
+        }
     }
 
     @Override