Merge changes If11fea2d,Ia7e1eebe,Iafa89db1 into ub-launcher3-master

* changes:
  Remove redundant resumeLastTaskForQuickstep() and use resumeLastTask() directly
  Fix some state issues with home and quick switch gestures
  Apply spring forces to animate to the final position for swipe home
diff --git a/go/quickstep/res/layout/task_item_view.xml b/go/quickstep/res/layout/task_item_view.xml
index ee67d49..048e9c5 100644
--- a/go/quickstep/res/layout/task_item_view.xml
+++ b/go/quickstep/res/layout/task_item_view.xml
@@ -21,15 +21,15 @@
     android:orientation="horizontal">
     <FrameLayout
         android:id="@+id/task_icon_and_thumbnail"
-        android:layout_width="@dimen/task_item_height"
-        android:layout_height="@dimen/task_item_height"
+        android:layout_width="@dimen/task_thumbnail_and_icon_view_size"
+        android:layout_height="@dimen/task_thumbnail_and_icon_view_size"
         android:layout_gravity="center_vertical"
         android:layout_marginHorizontal="8dp"
         android:layout_marginVertical="@dimen/task_item_half_vert_margin">
         <ImageView
             android:id="@+id/task_thumbnail"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
+            android:layout_width="@dimen/task_thumbnail_width"
+            android:layout_height="@dimen/task_thumbnail_height"
             android:layout_gravity="top|start"/>
         <ImageView
             android:id="@+id/task_icon"
diff --git a/go/quickstep/res/values/dimens.xml b/go/quickstep/res/values/dimens.xml
index 28cc1eb..7269704 100644
--- a/go/quickstep/res/values/dimens.xml
+++ b/go/quickstep/res/values/dimens.xml
@@ -15,7 +15,9 @@
      limitations under the License.
 -->
 <resources>
-    <dimen name="task_item_height">60dp</dimen>
     <dimen name="task_item_half_vert_margin">8dp</dimen>
+    <dimen name="task_thumbnail_and_icon_view_size">60dp</dimen>
+    <dimen name="task_thumbnail_height">60dp</dimen>
+    <dimen name="task_thumbnail_width">36dp</dimen>
     <dimen name="task_icon_size">36dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 5382607..cec12a8 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -43,7 +43,7 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
         return new float[] {1f, 0f};
     }
 
diff --git a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 784af7d..0b12ab0 100644
--- a/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/go/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -16,16 +16,15 @@
 package com.android.launcher3.uioverrides;
 
 import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
-import static com.android.quickstep.views.IconRecentsView.TRANSLATION_Y_FACTOR;
 
 import android.util.FloatProperty;
 
-import androidx.annotation.NonNull;
-
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherRecentsToActivityHelper;
 import com.android.quickstep.views.IconRecentsView;
 
+import androidx.annotation.NonNull;
+
 /**
  * State handler for Go's {@link IconRecentsView}.
  */
@@ -39,11 +38,6 @@
     }
 
     @Override
-    FloatProperty<IconRecentsView> getTranslationYFactorProperty() {
-        return TRANSLATION_Y_FACTOR;
-    }
-
-    @Override
     FloatProperty<IconRecentsView> getContentAlphaProperty() {
         return CONTENT_ALPHA;
     }
diff --git a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
index f199643..defed84 100644
--- a/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
+++ b/go/quickstep/src/com/android/quickstep/AppToOverviewAnimationProvider.java
@@ -15,15 +15,29 @@
  */
 package com.android.quickstep;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_2;
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.quickstep.util.RemoteAnimationProvider.getLayer;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
 
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
+import android.graphics.Matrix;
+import android.graphics.Rect;
 import android.util.Log;
+import android.view.View;
+
+import androidx.annotation.NonNull;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.quickstep.util.MultiValueUpdateListener;
 import com.android.quickstep.util.RemoteAnimationProvider;
+import com.android.quickstep.views.IconRecentsView;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
 
 /**
  * Provider for the atomic remote window animation from the app to the overview.
@@ -33,13 +47,17 @@
 final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> implements
         RemoteAnimationProvider {
 
-    private static final long RECENTS_LAUNCH_DURATION = 250;
+    private static final long APP_TO_THUMBNAIL_FADE_DURATION = 50;
+    private static final long APP_SCALE_DOWN_DURATION = 400;
     private static final String TAG = "AppToOverviewAnimationProvider";
 
     private final ActivityControlHelper<T> mHelper;
+    private final int mTargetTaskId;
+    private IconRecentsView mRecentsView;
 
     AppToOverviewAnimationProvider(ActivityControlHelper<T> helper, int targetTaskId) {
         mHelper = helper;
+        mTargetTaskId = targetTaskId;
     }
 
     /**
@@ -54,35 +72,157 @@
                         false /* animate activity */, (controller) -> {
                             controller.dispatchOnStart();
                             ValueAnimator anim = controller.getAnimationPlayer()
-                                    .setDuration(RECENTS_LAUNCH_DURATION);
+                                    .setDuration(getRecentsLaunchDuration());
                             anim.setInterpolator(FAST_OUT_SLOW_IN);
                             anim.start();
                         });
         factory.onRemoteAnimationReceived(null);
-        factory.createActivityController(RECENTS_LAUNCH_DURATION);
+        factory.createActivityController(getRecentsLaunchDuration());
+        mRecentsView = activity.getOverviewPanel();
         return false;
     }
 
     /**
-     * Create remote window animation from the currently running app to the overview panel.
+     * Create remote window animation from the currently running app to the overview panel. Should
+     * be called after {@link #onActivityReady}.
      *
      * @param targetCompats the target apps
      * @return animation from app to overview
      */
     @Override
     public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
-        //TODO: Implement the overview to app window animation for Go.
         AnimatorSet anim = new AnimatorSet();
-        anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
+        if (mRecentsView == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No recents view. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        RemoteAnimationTargetCompat recentsTarget = null;
+        RemoteAnimationTargetCompat closingAppTarget = null;
+
+        for (RemoteAnimationTargetCompat target : targetCompats) {
+            if (target.mode == MODE_OPENING) {
+                recentsTarget = target;
+            } else if (target.mode == MODE_CLOSING && target.taskId == mTargetTaskId) {
+                closingAppTarget = target;
+            }
+        }
+
+        if (closingAppTarget == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No closing app target. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+        if (recentsTarget == null) {
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No recents target. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        View thumbnailView = mRecentsView.getThumbnailViewForTask(mTargetTaskId);
+        if (thumbnailView == null) {
+            // TODO: We should either 1) guarantee the view is loaded before attempting this
+            // or 2) have a backup animation.
+            if (Log.isLoggable(TAG, Log.WARN)) {
+                Log.w(TAG, "No thumbnail view for running task. Using stub animation.");
+            }
+            anim.play(ValueAnimator.ofInt(0, 1).setDuration(getRecentsLaunchDuration()));
+            return anim;
+        }
+
+        playAppScaleDownAnim(anim, closingAppTarget, recentsTarget, thumbnailView);
+
         return anim;
     }
 
     /**
+     * Animate a closing app to scale down to the location of the thumbnail view in recents.
+     *
+     * @param anim animator set
+     * @param appTarget the app surface thats closing
+     * @param recentsTarget the surface containing recents
+     * @param thumbnailView the thumbnail view to animate to
+     */
+    private void playAppScaleDownAnim(@NonNull AnimatorSet anim,
+            @NonNull RemoteAnimationTargetCompat appTarget,
+            @NonNull RemoteAnimationTargetCompat recentsTarget, @NonNull View thumbnailView) {
+
+        // Identify where the entering remote app should animate to.
+        Rect endRect = new Rect();
+        thumbnailView.getGlobalVisibleRect(endRect);
+
+        Rect appBounds = appTarget.sourceContainerBounds;
+
+        ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 1);
+        valueAnimator.setDuration(APP_SCALE_DOWN_DURATION);
+
+        SyncRtSurfaceTransactionApplierCompat surfaceApplier =
+                new SyncRtSurfaceTransactionApplierCompat(thumbnailView);
+
+        // Keep recents visible throughout the animation.
+        SurfaceParams[] params = new SurfaceParams[2];
+        params[0] = new SurfaceParams(recentsTarget.leash, 1f, null /* matrix */,
+                null /* windowCrop */, getLayer(recentsTarget, MODE_OPENING), 0 /* cornerRadius */);
+
+        valueAnimator.addUpdateListener(new MultiValueUpdateListener() {
+            private final FloatProp mScaleX;
+            private final FloatProp mScaleY;
+            private final FloatProp mTranslationX;
+            private final FloatProp mTranslationY;
+            private final FloatProp mAlpha;
+
+            {
+                // Scale down and move to view location.
+                float endScaleX = ((float) endRect.width()) / appBounds.width();
+                mScaleX = new FloatProp(1f, endScaleX, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+                float endScaleY = ((float) endRect.height()) / appBounds.height();
+                mScaleY = new FloatProp(1f, endScaleY, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+                float endTranslationX = endRect.left -
+                        (appBounds.width() - thumbnailView.getWidth()) / 2.0f;
+                mTranslationX = new FloatProp(0, endTranslationX, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+                float endTranslationY = endRect.top -
+                        (appBounds.height() - thumbnailView.getHeight()) / 2.0f;
+                mTranslationY = new FloatProp(0, endTranslationY, 0, APP_SCALE_DOWN_DURATION,
+                        ACCEL_DEACCEL);
+
+                // Fade out quietly near the end to be replaced by the real view.
+                mAlpha = new FloatProp(1.0f, 0,
+                        APP_SCALE_DOWN_DURATION - APP_TO_THUMBNAIL_FADE_DURATION,
+                        APP_TO_THUMBNAIL_FADE_DURATION, ACCEL_2);
+            }
+
+            @Override
+            public void onUpdate(float percent) {
+                Matrix m = new Matrix();
+                m.setScale(mScaleX.value, mScaleY.value,
+                        appBounds.width() / 2.0f, appBounds.height() / 2.0f);
+                m.postTranslate(mTranslationX.value, mTranslationY.value);
+
+                params[1] = new SurfaceParams(appTarget.leash, mAlpha.value, m,
+                        null /* windowCrop */, getLayer(appTarget, MODE_CLOSING),
+                        0 /* cornerRadius */);
+                surfaceApplier.scheduleApply(params);
+            }
+        });
+        anim.play(valueAnimator);
+    }
+
+    /**
      * Get duration of animation from app to overview.
      *
      * @return duration of animation
      */
     long getRecentsLaunchDuration() {
-        return RECENTS_LAUNCH_DURATION;
+        return APP_SCALE_DOWN_DURATION;
     }
 }
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index d55d2e5..40db7dd 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -21,6 +21,7 @@
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherInitListener;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.quickstep.views.IconRecentsView;
@@ -38,10 +39,14 @@
     public AnimationFactory prepareRecentsUI(Launcher activity,
             boolean activityVisible, boolean animateActivity,
             Consumer<AnimatorPlaybackController> callback) {
+        LauncherState fromState = activity.getStateManager().getState();
         //TODO: Implement this based off where the recents view needs to be for app => recents anim.
         return new AnimationFactory() {
             @Override
-            public void createActivityController(long transitionLength) {}
+            public void createActivityController(long transitionLength) {
+                callback.accept(activity.getStateManager().createAnimationToNewWorkspace(
+                        fromState, OVERVIEW, transitionLength));
+            }
 
             @Override
             public void onTransitionCancelled() {}
diff --git a/go/quickstep/src/com/android/quickstep/TaskAdapter.java b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
index 4f3d1e4..9a2c0f8 100644
--- a/go/quickstep/src/com/android/quickstep/TaskAdapter.java
+++ b/go/quickstep/src/com/android/quickstep/TaskAdapter.java
@@ -15,10 +15,12 @@
  */
 package com.android.quickstep;
 
+import android.util.ArrayMap;
 import android.view.LayoutInflater;
 import android.view.ViewGroup;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView.Adapter;
 
 import com.android.launcher3.R;
@@ -36,6 +38,7 @@
     private static final int MAX_TASKS_TO_DISPLAY = 6;
     private static final String TAG = "TaskAdapter";
     private final TaskListLoader mLoader;
+    private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
     private TaskInputController mInputController;
 
     public TaskAdapter(@NonNull TaskListLoader loader) {
@@ -46,6 +49,16 @@
         mInputController = inputController;
     }
 
+    /**
+     * Get task item view for a given task id if it's attached to the view.
+     *
+     * @param taskId task id to search for
+     * @return corresponding task item view if it's attached, null otherwise
+     */
+    public @Nullable TaskItemView getTaskItemView(int taskId) {
+        return mTaskIdToViewMap.get(taskId);
+    }
+
     @Override
     public TaskHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         TaskItemView itemView = (TaskItemView) LayoutInflater.from(parent.getContext())
@@ -63,6 +76,17 @@
             return;
         }
         holder.bindTask(tasks.get(position));
+
+    }
+
+    @Override
+    public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
+        mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
+    }
+
+    @Override
+    public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
+        mTaskIdToViewMap.remove(holder.getTask().key.id);
     }
 
     @Override
diff --git a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
index e294282..504f640 100644
--- a/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
+++ b/go/quickstep/src/com/android/quickstep/views/IconRecentsView.java
@@ -27,6 +27,7 @@
 import android.widget.FrameLayout;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.ItemTouchHelper;
 import androidx.recyclerview.widget.LinearLayoutManager;
 import androidx.recyclerview.widget.RecyclerView;
@@ -45,20 +46,6 @@
  */
 public final class IconRecentsView extends FrameLayout {
 
-    public static final FloatProperty<IconRecentsView> TRANSLATION_Y_FACTOR =
-            new FloatProperty<IconRecentsView>("translationYFactor") {
-
-                @Override
-                public void setValue(IconRecentsView view, float v) {
-                    view.setTranslationYFactor(v);
-                }
-
-                @Override
-                public Float get(IconRecentsView view) {
-                    return view.mTranslationYFactor;
-                }
-            };
-
     public static final FloatProperty<IconRecentsView> CONTENT_ALPHA =
             new FloatProperty<IconRecentsView>("contentAlpha") {
                 @Override
@@ -90,7 +77,6 @@
     private final TaskInputController mTaskInputController;
 
     private RecentsToActivityHelper mActivityHelper;
-    private float mTranslationYFactor;
     private RecyclerView mTaskRecyclerView;
     private View mEmptyView;
 
@@ -155,13 +141,18 @@
         });
     }
 
-    public void setTranslationYFactor(float translationFactor) {
-        mTranslationYFactor = translationFactor;
-        setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
-    }
-
-    private float computeTranslationYForFactor(float translationYFactor) {
-        return translationYFactor * (getPaddingBottom() - getPaddingTop());
+    /**
+     * Get the thumbnail view associated with a task for the purposes of animation.
+     *
+     * @param taskId task id of thumbnail view to get
+     * @return the thumbnail view for the task if attached, null otherwise
+     */
+    public @Nullable View getThumbnailViewForTask(int taskId) {
+        TaskItemView view = mTaskAdapter.getTaskItemView(taskId);
+        if (view == null) {
+            return null;
+        }
+        return view.getThumbnailView();
     }
 
     /**
diff --git a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
index 3818965..373f107 100644
--- a/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
+++ b/go/quickstep/src/com/android/quickstep/views/TaskItemView.java
@@ -19,6 +19,7 @@
 import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -76,4 +77,8 @@
     public void setThumbnail(Bitmap thumbnail) {
         mThumbnailView.setImageBitmap(thumbnail);
     }
+
+    public View getThumbnailView() {
+        return mThumbnailView;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
index fdb80da..f712753 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/BackgroundAppState.java
@@ -52,7 +52,7 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
         // Initialize the recents view scale to what it would be when starting swipe up
         RecentsView recentsView = launcher.getOverviewPanel();
         recentsView.getTaskSize(sTempRect);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
index 79e127a..2360eeb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -69,7 +69,7 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
+    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
         return new float[] {1f, 0f};
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 0b3bd6c..0d5574f 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -15,7 +15,6 @@
  */
 package com.android.launcher3.uioverrides;
 
-import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 
 import android.animation.ValueAnimator;
@@ -23,8 +22,6 @@
 import android.os.Build;
 import android.util.FloatProperty;
 
-import androidx.annotation.NonNull;
-
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
@@ -32,6 +29,8 @@
 import com.android.quickstep.views.LauncherRecentsView;
 import com.android.quickstep.views.RecentsView;
 
+import androidx.annotation.NonNull;
+
 /**
  * State handler for handling UI changes for {@link LauncherRecentsView}. In addition to managing
  * the basic view properties, this class also manages changes in the task visuals.
@@ -80,11 +79,6 @@
     }
 
     @Override
-    FloatProperty<LauncherRecentsView> getTranslationYFactorProperty() {
-        return TRANSLATION_Y_FACTOR;
-    }
-
-    @Override
     FloatProperty<RecentsView> getContentAlphaProperty() {
         return CONTENT_ALPHA;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
index 109a4c5..c00b4dc 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AssistantTouchConsumer.java
@@ -22,59 +22,70 @@
 import static android.view.MotionEvent.ACTION_POINTER_UP;
 import static android.view.MotionEvent.ACTION_UP;
 
+import android.animation.ValueAnimator;
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.PointF;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.Log;
-import android.view.Display;
 import android.view.MotionEvent;
-import android.view.Surface;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
+import com.android.launcher3.anim.Interpolators;
+import com.android.quickstep.util.MotionPauseDetector;
 import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.system.NavigationBarCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.launcher3.R;
+import com.android.systemui.shared.system.NavigationBarCompat;
 
 /**
  * Touch consumer for handling events to launch assistant from launcher
  */
 public class AssistantTouchConsumer implements InputConsumer {
     private static final String TAG = "AssistantTouchConsumer";
+    private static final long RETRACT_ANIMATION_DURATION_MS = 300;
+
+    /* The assistant touch consume competes with quick switch InputConsumer gesture. The delegate
+     * can be chosen to run if the angle passing the slop is lower than the threshold angle. When
+     * this occurs, the state changes to {@link #STATE_DELEGATE_ACTIVE} where the next incoming
+     * motion events are handled by the delegate instead of the assistant touch consumer. If the
+     * angle is higher than the threshold, the state will change to {@link #STATE_ASSISTANT_ACTIVE}.
+     */
+    private static final int STATE_INACTIVE = 0;
+    private static final int STATE_ASSISTANT_ACTIVE = 1;
+    private static final int STATE_DELEGATE_ACTIVE = 2;
 
     private final PointF mDownPos = new PointF();
     private final PointF mLastPos = new PointF();
+    private final PointF mStartDragPos = new PointF();
+
     private int mActivePointerId = -1;
-
-    private final int mDisplayRotation;
-    private final Rect mStableInsets = new Rect();
-
-    private final float mDragSlop;
-    private final float mTouchSlop;
-    private final float mThreshold;
-
-    private float mStartDisplacement;
-    private boolean mPassedDragSlop;
-    private boolean mPassedTouchSlop;
-    private long mPassedTouchSlopTime;
+    private boolean mPassedSlop;
     private boolean mLaunchedAssistant;
+    private float mDistance;
+    private float mTimeFraction;
+    private long mDragTime;
     private float mLastProgress;
+    private int mState;
 
+    private final float mDistThreshold;
+    private final long mTimeThreshold;
+    private final int mAngleThreshold;
+    private final float mSlop;
+    private final MotionPauseDetector mMotionPauseDetector;
     private final ISystemUiProxy mSysUiProxy;
+    private final InputConsumer mConsumerDelegate;
 
-    public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy) {
+    public AssistantTouchConsumer(Context context, ISystemUiProxy systemUiProxy,
+            InputConsumer delegate) {
+        final Resources res = context.getResources();
         mSysUiProxy = systemUiProxy;
-
-        mDragSlop = NavigationBarCompat.getQuickStepDragSlopPx();
-        mTouchSlop = NavigationBarCompat.getQuickStepTouchSlopPx();
-        mThreshold = context.getResources().getDimension(R.dimen.gestures_assistant_threshold);
-
-        Display display = context.getSystemService(WindowManager.class).getDefaultDisplay();
-        mDisplayRotation = display.getRotation();
-        WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
+        mConsumerDelegate = delegate;
+        mMotionPauseDetector = new MotionPauseDetector(context);
+        mDistThreshold = res.getDimension(R.dimen.gestures_assistant_drag_threshold);
+        mTimeThreshold = res.getInteger(R.integer.assistant_gesture_min_time_threshold);
+        mAngleThreshold = res.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
+        mSlop = NavigationBarCompat.getQuickScrubTouchSlopPx();
+        mState = STATE_INACTIVE;
     }
 
     @Override
@@ -83,14 +94,28 @@
     }
 
     @Override
+    public boolean isActive() {
+        return mState != STATE_INACTIVE;
+    }
+
+    @Override
     public void onMotionEvent(MotionEvent ev) {
         // TODO add logging
+
         switch (ev.getActionMasked()) {
             case ACTION_DOWN: {
                 mActivePointerId = ev.getPointerId(0);
                 mDownPos.set(ev.getX(), ev.getY());
                 mLastPos.set(mDownPos);
-                mLastProgress = -1;
+                mTimeFraction = 0;
+
+                // Detect when the gesture decelerates to start the assistant
+                mMotionPauseDetector.setOnMotionPauseListener(isPaused -> {
+                    if (isPaused && mState == STATE_ASSISTANT_ACTIVE) {
+                        mTimeFraction = 1;
+                        updateAssistantProgress();
+                    }
+                });
                 break;
             }
             case ACTION_POINTER_UP: {
@@ -107,94 +132,101 @@
                 break;
             }
             case ACTION_MOVE: {
+                if (mState == STATE_DELEGATE_ACTIVE) {
+                    break;
+                }
                 int pointerIndex = ev.findPointerIndex(mActivePointerId);
                 if (pointerIndex == -1) {
                     break;
                 }
                 mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
-                float displacement = getDisplacement(ev);
 
-                if (!mPassedDragSlop) {
-                    // Normal gesture, ensure we pass the drag slop before we start tracking
-                    // the gesture
-                    if (Math.abs(displacement) > mDragSlop) {
-                        mPassedDragSlop = true;
-                        mStartDisplacement = displacement;
-                        mPassedTouchSlopTime = SystemClock.uptimeMillis();
-                    }
-                }
+                if (!mPassedSlop) {
+                    // Normal gesture, ensure we pass the slop before we start tracking the gesture
+                    if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) > mSlop) {
+                        mPassedSlop = true;
+                        mStartDragPos.set(mLastPos.x, mLastPos.y);
+                        mDragTime = SystemClock.uptimeMillis();
 
-                if (!mPassedTouchSlop) {
-                    if (Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y) >=
-                        mTouchSlop) {
-                        mPassedTouchSlop = true;
-                        if (!mPassedDragSlop) {
-                            mPassedDragSlop = true;
-                            mStartDisplacement = displacement;
-                            mPassedTouchSlopTime = SystemClock.uptimeMillis();
+                        // Determine if angle is larger than threshold for assistant detection
+                        float angle = (float) Math.toDegrees(
+                                Math.atan2(mDownPos.y - mLastPos.y, mDownPos.x - mLastPos.x));
+                        angle = angle > 90 ? 180 - angle : angle;
+                        if (angle > mAngleThreshold) {
+                            mState = STATE_ASSISTANT_ACTIVE;
+
+                            if (mConsumerDelegate != null) {
+                                // Send cancel event
+                                MotionEvent event = MotionEvent.obtain(ev);
+                                event.setAction(MotionEvent.ACTION_CANCEL);
+                                mConsumerDelegate.onMotionEvent(event);
+                                event.recycle();
+                            }
+                        } else {
+                            mState = STATE_DELEGATE_ACTIVE;
                         }
                     }
-                }
-
-                if (mPassedDragSlop) {
-                    // Move
-                    float distance = mStartDisplacement - displacement;
-                    if (distance >= 0) {
-                        onAssistantProgress(distance / mThreshold);
+                } else {
+                    // Movement
+                    mDistance = (float) Math.hypot(mLastPos.x - mStartDragPos.x,
+                            mLastPos.y - mStartDragPos.y);
+                    mMotionPauseDetector.addPosition(mDistance, 0);
+                    if (mDistance >= 0) {
+                        final long diff = SystemClock.uptimeMillis() - mDragTime;
+                        mTimeFraction = Math.min(diff * 1f / mTimeThreshold, 1);
+                        updateAssistantProgress();
                     }
                 }
                 break;
             }
             case ACTION_CANCEL:
-                break;
-            case ACTION_UP: {
-                if (ev.getEventTime() - mPassedTouchSlopTime < ViewConfiguration.getTapTimeout()) {
-                    onAssistantProgress(1);
+            case ACTION_UP:
+                if (mState != STATE_DELEGATE_ACTIVE && !mLaunchedAssistant) {
+                    ValueAnimator animator = ValueAnimator.ofFloat(mLastProgress, 0)
+                            .setDuration(RETRACT_ANIMATION_DURATION_MS);
+                    animator.addUpdateListener(valueAnimator -> {
+                            float progress = (float) valueAnimator.getAnimatedValue();
+                            try {
+                                mSysUiProxy.onAssistantProgress(progress);
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "Failed to send SysUI start/send assistant progress: "
+                                        + progress, e);
+                            }
+                    });
+                    animator.setInterpolator(Interpolators.DEACCEL_2);
+                    animator.start();
                 }
-
+                mMotionPauseDetector.clear();
                 break;
-            }
+        }
+
+        if (mState != STATE_ASSISTANT_ACTIVE && mConsumerDelegate != null) {
+            mConsumerDelegate.onMotionEvent(ev);
         }
     }
 
-    private void onAssistantProgress(float progress) {
-        if (mLastProgress == progress) {
-            return;
-        }
-        try {
-            mSysUiProxy.onAssistantProgress(Math.max(0, Math.min(1, progress)));
-            if (progress >= 1 && !mLaunchedAssistant) {
-                mSysUiProxy.startAssistant(new Bundle());
-                mLaunchedAssistant = true;
-            }
+    private void updateAssistantProgress() {
+        if (!mLaunchedAssistant) {
+            float progress = Math.min(mDistance * 1f / mDistThreshold, 1) * mTimeFraction;
             mLastProgress = progress;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to notify SysUI to start/send assistant progress: " + progress, e);
+            try {
+                mSysUiProxy.onAssistantProgress(progress);
+
+                if (mDistance >= mDistThreshold && mTimeFraction >= 1) {
+                    mSysUiProxy.startAssistant(new Bundle());
+                    mLaunchedAssistant = true;
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to send SysUI start/send assistant progress: " + progress, e);
+            }
         }
     }
 
-    private boolean isNavBarOnRight() {
-        return mDisplayRotation == Surface.ROTATION_90 && mStableInsets.right > 0;
-    }
-
-    private boolean isNavBarOnLeft() {
-        return mDisplayRotation == Surface.ROTATION_270 && mStableInsets.left > 0;
-    }
-
-    private float getDisplacement(MotionEvent ev) {
-        float eventX = ev.getX();
-        float eventY = ev.getY();
-        float displacement = eventY - mDownPos.y;
-        if (isNavBarOnRight()) {
-            displacement = eventX - mDownPos.x;
-        } else if (isNavBarOnLeft()) {
-            displacement = mDownPos.x - eventX;
-        }
-        return displacement;
-    }
-
-    static boolean withinTouchRegion(Context context, float x) {
-        return x > context.getResources().getDisplayMetrics().widthPixels
-                - context.getResources().getDimension(R.dimen.gestures_assistant_width);
+    static boolean withinTouchRegion(Context context, MotionEvent ev) {
+        final Resources res = context.getResources();
+        final int width = res.getDisplayMetrics().widthPixels;
+        final int height = res.getDisplayMetrics().heightPixels;
+        final int size = res.getDimensionPixelSize(R.dimen.gestures_assistant_size);
+        return (ev.getX() > width - size || ev.getX() < size) && ev.getY() > height - size;
     }
 }
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 b0bd71b..279b83c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -56,9 +56,7 @@
 import com.android.launcher3.anim.SpringObjectAnimator;
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
-import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.launcher3.views.FloatingIconView;
 import com.android.quickstep.util.ClipAnimationHelper;
 import com.android.quickstep.util.LayoutUtils;
@@ -307,7 +305,7 @@
         // starting to line up the side pages during swipe up)
         float prevRvScale = recentsView.getScaleX();
         float prevRvTransY = recentsView.getTranslationY();
-        float targetRvScale = endState.getOverviewScaleAndTranslationYFactor(launcher)[0];
+        float targetRvScale = endState.getOverviewScaleAndTranslationY(launcher)[0];
         SCALE_PROPERTY.set(recentsView, targetRvScale);
         recentsView.setTranslationY(0);
         ClipAnimationHelper clipHelper = new ClipAnimationHelper(launcher);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
index 9506bfa..c8dcf80 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OtherActivityInputConsumer.java
@@ -23,7 +23,7 @@
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 import static com.android.launcher3.util.RaceConditionTracker.ENTER;
 import static com.android.launcher3.util.RaceConditionTracker.EXIT;
-import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.quickstep.TouchInteractionService.TOUCH_INTERACTION_LOG;
 import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
 
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 cce5cb3..02ffdb3 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewInputConsumer.java
@@ -30,6 +30,7 @@
 import android.view.ViewConfiguration;
 
 import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.views.BaseDragLayer;
 import com.android.quickstep.util.CachedEventDispatcher;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -139,7 +140,7 @@
             return;
         }
         int flags = ev.getEdgeFlags();
-        ev.setEdgeFlags(flags | TouchInteractionService.EDGE_NAV_BAR);
+        ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
         ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
         if (ev.getAction() == ACTION_DOWN) {
             mTarget.onInterceptTouchEvent(ev);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 6d608ee..8fe0461 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -74,8 +74,6 @@
     public static final EventLogArray TOUCH_INTERACTION_LOG =
             new EventLogArray("touch_interaction_log", 40);
 
-    public static final int EDGE_NAV_BAR = 1 << 8;
-
     private static final String TAG = "TouchInteractionService";
 
     private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@@ -313,31 +311,36 @@
             mSwipeSharedState.clearAllState();
         }
 
+        final ActivityControlHelper activityControl =
+                mOverviewComponentObserver.getActivityControlHelper();
         if (runningTaskInfo == null && !mSwipeSharedState.goingToLauncher) {
             return InputConsumer.NO_OP;
         } else if (mAssistantAvailable && mOverviewInteractionState.isSwipeUpGestureEnabled()
                 && FeatureFlags.ENABLE_ASSISTANT_GESTURE.get()
-                && AssistantTouchConsumer.withinTouchRegion(this, event.getX())) {
-            return new AssistantTouchConsumer(this, mRecentsModel.getSystemUiProxy());
-        } else if (mSwipeSharedState.goingToLauncher ||
-                mOverviewComponentObserver.getActivityControlHelper().isResumed()) {
-            return OverviewInputConsumer.newInstance(
-                    mOverviewComponentObserver.getActivityControlHelper(), false);
+                && AssistantTouchConsumer.withinTouchRegion(this, event)) {
+            return new AssistantTouchConsumer(this, mISystemUiProxy, !activityControl.isResumed()
+                            ? createOtherActivityInputConsumer(event, runningTaskInfo) : null);
+        } else if (mSwipeSharedState.goingToLauncher || activityControl.isResumed()) {
+            return OverviewInputConsumer.newInstance(activityControl, false);
         } else if (ENABLE_QUICKSTEP_LIVE_TILE.get() &&
-                mOverviewComponentObserver.getActivityControlHelper().isInLiveTileMode()) {
-            return OverviewInputConsumer.newInstance(
-                    mOverviewComponentObserver.getActivityControlHelper(), false);
+                activityControl.isInLiveTileMode()) {
+            return OverviewInputConsumer.newInstance(activityControl, false);
         } else {
-            ActivityControlHelper activityControl =
-                    mOverviewComponentObserver.getActivityControlHelper();
-            boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
-            return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
-                    mOverviewComponentObserver.getOverviewIntent(), activityControl,
-                    shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
-                    this::onConsumerInactive, mSwipeSharedState);
+            return createOtherActivityInputConsumer(event, runningTaskInfo);
         }
     }
 
+    private OtherActivityInputConsumer createOtherActivityInputConsumer(MotionEvent event,
+            RunningTaskInfo runningTaskInfo) {
+        final ActivityControlHelper activityControl =
+                mOverviewComponentObserver.getActivityControlHelper();
+        boolean shouldDefer = activityControl.deferStartingActivity(mActiveNavBarRegion, event);
+        return new OtherActivityInputConsumer(this, runningTaskInfo, mRecentsModel,
+                mOverviewComponentObserver.getOverviewIntent(), activityControl,
+                shouldDefer, mOverviewCallbacks, mTaskOverlayFactory, mInputConsumer,
+                this::onConsumerInactive, mSwipeSharedState);
+    }
+
     /**
      * To be called by the consumer when it's no longer active.
      */
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 97bce5e..8f2a2d7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -56,27 +56,6 @@
 @TargetApi(Build.VERSION_CODES.O)
 public class LauncherRecentsView extends RecentsView<Launcher> {
 
-    public static final FloatProperty<LauncherRecentsView> TRANSLATION_Y_FACTOR =
-            new FloatProperty<LauncherRecentsView>("translationYFactor") {
-
-                @Override
-                public void setValue(LauncherRecentsView view, float v) {
-                    view.setTranslationYFactor(v);
-                }
-
-                @Override
-                public Float get(LauncherRecentsView view) {
-                    return view.mTranslationYFactor;
-                }
-            };
-
-    /**
-     * A ratio representing the view's relative placement within its padded space. For example, 0
-     * is top aligned and 0.5 is centered vertically.
-     */
-    @ViewDebug.ExportedProperty(category = "launcher")
-    private float mTranslationYFactor;
-
     private final TransformParams mTransformParams = new TransformParams();
     private ChipsContainer mChipsContainer;
 
@@ -104,12 +83,6 @@
     }
 
     @Override
-    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-        super.onLayout(changed, left, top, right, bottom);
-        setTranslationYFactor(mTranslationYFactor);
-    }
-
-    @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         mChipsContainer = mActivity.findViewById(R.id.hints);
@@ -117,9 +90,9 @@
         params.bottomMargin = mActivity.getDeviceProfile().chipHintBottomMarginPx;
     }
 
-    public void setTranslationYFactor(float translationFactor) {
-        mTranslationYFactor = translationFactor;
-        setTranslationY(computeTranslationYForFactor(mTranslationYFactor));
+    @Override
+    public void setTranslationY(float translationY) {
+        super.setTranslationY(translationY);
         if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
             LauncherState state = mActivity.getStateManager().getState();
             if (state == OVERVIEW || state == ALL_APPS) {
@@ -128,10 +101,6 @@
         }
     }
 
-    public float computeTranslationYForFactor(float translationYFactor) {
-        return translationYFactor * (getPaddingBottom() - getPaddingTop());
-    }
-
     public void setHintVisibility(float v) {
         if (mChipsContainer != null && ENABLE_HINTS_IN_OVERVIEW.get()) {
             mChipsContainer.setHintVisibility(v);
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 b7a708f..37febf9a 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
@@ -27,7 +27,7 @@
 import static com.android.launcher3.uioverrides.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.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.quickstep.util.ClipAnimationHelper.TransformParams;
 
 import android.animation.Animator;
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 3fbfcdd..a966698 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -26,4 +26,8 @@
          determines how many thumbnails will be fetched in the background. -->
     <integer name="recentsThumbnailCacheSize">3</integer>
     <integer name="recentsIconCacheSize">12</integer>
+
+    <!-- Assistant Gesture -->
+    <integer name="assistant_gesture_min_time_threshold">200</integer>
+    <integer name="assistant_gesture_corner_deg_threshold">30</integer>
 </resources>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index f5e5dd3..9c97c8c 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -66,6 +66,6 @@
     <dimen name="shelf_surface_offset">24dp</dimen>
 
     <!-- Assistant Gestures -->
-    <dimen name="gestures_assistant_width">70dp</dimen>
-    <dimen name="gestures_assistant_threshold">200dp</dimen>
+    <dimen name="gestures_assistant_size">28dp</dimen>
+    <dimen name="gestures_assistant_drag_threshold">70dp</dimen>
 </resources>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 1eaa8bc..5ae562e 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -78,8 +78,9 @@
     }
 
     @Override
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        return new float[] {0.9f, -0.2f};
+    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
+        float slightParallax = -launcher.getDeviceProfile().allAppsCellHeightPx * 0.3f;
+        return new float[] {0.9f, slightParallax};
     }
 
     @Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index df9dbe4..e74d84d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -26,8 +26,6 @@
 import android.view.View;
 import android.view.animation.Interpolator;
 
-import androidx.annotation.NonNull;
-
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherState;
 import com.android.launcher3.LauncherStateManager.AnimationConfig;
@@ -35,6 +33,8 @@
 import com.android.launcher3.anim.AnimatorSetBuilder;
 import com.android.launcher3.anim.PropertySetter;
 
+import androidx.annotation.NonNull;
+
 /**
  * State handler for recents view. Manages UI changes and animations for recents view based off the
  * current {@link LauncherState}.
@@ -53,9 +53,9 @@
 
     @Override
     public void setState(@NonNull LauncherState state) {
-        float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher);
-        SCALE_PROPERTY.set(mRecentsView, scaleTranslationYFactor[0]);
-        getTranslationYFactorProperty().set(mRecentsView, scaleTranslationYFactor[1]);
+        float[] scaleTranslationY = state.getOverviewScaleAndTranslationY(mLauncher);
+        SCALE_PROPERTY.set(mRecentsView, scaleTranslationY[0]);
+        mRecentsView.setTranslationY(scaleTranslationY[1]);
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
     }
 
@@ -79,11 +79,11 @@
     void setStateWithAnimationInternal(@NonNull final LauncherState toState,
             @NonNull AnimatorSetBuilder builder, @NonNull AnimationConfig config) {
         PropertySetter setter = config.getPropertySetter(builder);
-        float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
+        float[] scaleTranslationY = toState.getOverviewScaleAndTranslationY(mLauncher);
         Interpolator scaleAndTransYInterpolator = getScaleAndTransYInterpolator(toState, builder);
-        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0],
+        setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationY[0],
                 scaleAndTransYInterpolator);
-        setter.setFloat(mRecentsView, getTranslationYFactorProperty(), scaleTranslationYFactor[1],
+        setter.setFloat(mRecentsView, View.TRANSLATION_Y, scaleTranslationY[1],
                 scaleAndTransYInterpolator);
         setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
                 builder.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
@@ -102,13 +102,6 @@
     }
 
     /**
-     * Get property for translation Y factor for the recents view.
-     *
-     * @return the float property for the recents view
-     */
-    abstract FloatProperty getTranslationYFactorProperty();
-
-    /**
      * 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/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
index fd4bf9b..086cbdb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java
@@ -2,7 +2,7 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.quickstep.TouchInteractionService.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 
 import android.view.MotionEvent;
 
diff --git a/quickstep/src/com/android/quickstep/TestInformationProvider.java b/quickstep/src/com/android/quickstep/TestInformationProvider.java
index 0c478d2..e57d3ec 100644
--- a/quickstep/src/com/android/quickstep/TestInformationProvider.java
+++ b/quickstep/src/com/android/quickstep/TestInformationProvider.java
@@ -25,6 +25,9 @@
 
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherState;
 import com.android.launcher3.TestProtocol;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.uioverrides.OverviewState;
@@ -68,6 +71,9 @@
             final Context context = getContext();
             final DeviceProfile deviceProfile = InvariantDeviceProfile.INSTANCE.
                     get(context).getDeviceProfile(context);
+            final LauncherAppState launcherAppState = LauncherAppState.getInstanceNoCreate();
+            final Launcher launcher = launcherAppState != null ?
+                    (Launcher) launcherAppState.getModel().getCallback() : null;
 
             switch (method) {
                 case TestProtocol.REQUEST_HOME_TO_OVERVIEW_SWIPE_HEIGHT: {
@@ -76,12 +82,35 @@
                     response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
                     break;
                 }
+
                 case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
                     final float swipeHeight =
                             LayoutUtils.getShelfTrackingDistance(context, deviceProfile);
                     response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
                     break;
                 }
+
+                case TestProtocol.REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT: {
+                    if (launcher == null) return null;
+
+                    final float progress = LauncherState.OVERVIEW.getVerticalProgress(launcher)
+                            - LauncherState.ALL_APPS.getVerticalProgress(launcher);
+                    final float distance =
+                            launcher.getAllAppsController().getShiftRange() * progress;
+                    response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
+                    break;
+                }
+
+                case TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT: {
+                    if (launcher == null) return null;
+
+                    final float progress = LauncherState.NORMAL.getVerticalProgress(launcher)
+                            - LauncherState.ALL_APPS.getVerticalProgress(launcher);
+                    final float distance =
+                            launcher.getAllAppsController().getShiftRange() * progress;
+                    response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) distance);
+                    break;
+                }
             }
             return response;
         }
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 6582df2..f081303 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -20,7 +20,10 @@
 import static com.android.launcher3.Utilities.getDevicePrefs;
 
 import android.annotation.TargetApi;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -114,6 +117,7 @@
 
     private final ArrayList<OnIDPChangeListener> mChangeListeners = new ArrayList<>();
     private ConfigMonitor mConfigMonitor;
+    private OverlayMonitor mOverlayMonitor;
 
     @VisibleForTesting
     public InvariantDeviceProfile() {}
@@ -131,6 +135,7 @@
         defaultLayoutId = p.defaultLayoutId;
         demoModeLayoutId = p.demoModeLayoutId;
         mExtraAttrs = p.mExtraAttrs;
+        mOverlayMonitor = p.mOverlayMonitor;
     }
 
     @TargetApi(23)
@@ -138,8 +143,12 @@
         initGrid(context, Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null));
         mConfigMonitor = new ConfigMonitor(context,
                 APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
+        mOverlayMonitor = new OverlayMonitor(context);
     }
 
+    /**
+     * This constructor should NOT have any monitors by design.
+     */
     public InvariantDeviceProfile(Context context, String gridName) {
         String newName = initGrid(context, gridName);
         if (newName == null || !newName.equals(gridName)) {
@@ -555,4 +564,20 @@
             return this;
         }
     }
+
+    private class OverlayMonitor extends BroadcastReceiver {
+
+        private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
+
+        OverlayMonitor(Context context) {
+            IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
+            filter.addDataScheme("package");
+            context.registerReceiver(this, filter);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            onConfigChanged(context);
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index cee1c26..875288a 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -192,13 +192,7 @@
         return getWorkspaceScaleAndTranslation(launcher);
     }
 
-    /**
-     * Returns 2 floats designating how to transition overview:
-     *   scale for the current and adjacent pages
-     *   translationY factor where 0 is top aligned and 0.5 is centered vertically
-     */
-    public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) {
-        // TODO: Simplify to use a constant value instead of a factor.
+    public float[] getOverviewScaleAndTranslationY(Launcher launcher) {
         return new float[] {1.1f, 0f};
     }
 
diff --git a/src/com/android/launcher3/TestProtocol.java b/src/com/android/launcher3/TestProtocol.java
index 5f752cc..4eb3627 100644
--- a/src/com/android/launcher3/TestProtocol.java
+++ b/src/com/android/launcher3/TestProtocol.java
@@ -36,4 +36,8 @@
             "home-to-overview-swipe-height";
     public static final String REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT =
             "background-to-overview-swipe-height";
+    public static final String REQUEST_ALL_APPS_TO_OVERVIEW_SWIPE_HEIGHT =
+            "all-apps-to-overview-swipe-height";
+    public static final String REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT =
+            "home-to-all-apps-swipe-height";
 }
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7036639..dd755cb 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -54,6 +54,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.TypedValue;
+import android.view.MotionEvent;
 import android.view.View;
 import android.view.animation.Interpolator;
 
@@ -115,6 +116,11 @@
     public static final int SINGLE_FRAME_MS = 16;
 
     /**
+     * Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
+     */
+    public static final int EDGE_NAV_BAR = 1 << 8;
+
+    /**
      * Indicates if the device has a debug build. Should only be used to store additional info or
      * add extra logging and not for changing the app behavior.
      */
diff --git a/src/com/android/launcher3/anim/SpringObjectAnimator.java b/src/com/android/launcher3/anim/SpringObjectAnimator.java
index cc70e32..f74590b 100644
--- a/src/com/android/launcher3/anim/SpringObjectAnimator.java
+++ b/src/com/android/launcher3/anim/SpringObjectAnimator.java
@@ -15,6 +15,8 @@
  */
 package com.android.launcher3.anim;
 
+import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
+
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
@@ -32,8 +34,6 @@
 import androidx.dynamicanimation.animation.SpringAnimation;
 import androidx.dynamicanimation.animation.SpringForce;
 
-import static com.android.launcher3.config.FeatureFlags.QUICKSTEP_SPRINGS;
-
 /**
  * This animator allows for an object's property to be be controlled by an {@link ObjectAnimator} or
  * a {@link SpringAnimation}. It extends ValueAnimator so it can be used in an AnimatorSet.
@@ -51,9 +51,9 @@
     private SpringProperty<T> mProperty;
 
     private ArrayList<AnimatorListener> mListeners;
-    private boolean mSpringEnded = false;
-    private boolean mAnimatorEnded = false;
-    private boolean mEnded = false;
+    private boolean mSpringEnded = true;
+    private boolean mAnimatorEnded = true;
+    private boolean mEnded = true;
 
     private static final FloatPropertyCompat<ProgressInterface> sFloatProperty =
             new FloatPropertyCompat<ProgressInterface>("springObjectAnimator") {
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index b0af4c6..10ecc4f 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -22,6 +22,7 @@
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
 import static com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 
 import android.animation.AnimatorSet;
 import android.animation.LayoutTransition;
@@ -167,7 +168,8 @@
     public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
             BaseDragLayer dl = getPopupContainer();
-            if (!dl.isEventOverView(this, ev)) {
+            final boolean cameFromNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
+            if (!cameFromNavBar && !dl.isEventOverView(this, ev)) {
                 mLauncher.getUserEventDispatcher().logActionTapOutside(
                         LoggerUtils.newContainerTarget(ContainerType.DEEPSHORTCUTS));
                 close(true);
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
index 12280f8..12d35e9 100644
--- a/src/com/android/launcher3/util/ConfigMonitor.java
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -41,8 +41,6 @@
 
     private static final String TAG = "ConfigMonitor";
 
-    private final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED";
-
     private final Point mTmpPoint1 = new Point();
     private final Point mTmpPoint2 = new Point();
 
@@ -78,11 +76,6 @@
         // Listen for configuration change
         mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
 
-        // Listen for {@link OverlayManager} change
-        IntentFilter filter = new IntentFilter(ACTION_OVERLAY_CHANGED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(this, filter);
-
         // Listen for display manager change
         mContext.getSystemService(DisplayManager.class)
                 .registerDisplayListener(this, new Handler(UiThreadHelper.getBackgroundLooper()));
@@ -91,12 +84,6 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         Configuration config = context.getResources().getConfiguration();
-        // TODO: when overlay manager service encodes more information to the Uri such as category
-        // of the overlay, only listen to the ones that are of interest to launcher.
-        if (intent != null && ACTION_OVERLAY_CHANGED.equals(intent.getAction())) {
-            Log.d(TAG, "Overlay changed.");
-            notifyChange();
-        }
         if (mFontScale != config.fontScale || mDensity != config.densityDpi) {
             Log.d(TAG, "Configuration changed.");
             notifyChange();
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 48cf9e8..e259cfe 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -18,8 +18,10 @@
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_FLAVOR;
 import static com.android.launcher3.Utilities.EXTRA_WALLPAPER_OFFSET;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.text.TextUtils;
@@ -150,7 +152,7 @@
         RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
 
         ArrayList<OptionItem> options = new ArrayList<>();
-        int res = FeatureFlags.STYLE_WALLPAPER.get() ?
+        int res = FeatureFlags.STYLE_WALLPAPER.get() && existsStyleWallpapers(launcher) ?
                 R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
         options.add(new OptionItem(res, R.drawable.ic_wallpaper,
                 ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
@@ -164,6 +166,14 @@
         show(launcher, target, options);
     }
 
+    private static boolean existsStyleWallpapers(Launcher launcher) {
+        Intent intent = new Intent(Intent.ACTION_SET_WALLPAPER);
+        intent.setComponent(new ComponentName(launcher.getString(R.string.wallpaper_picker_package),
+                "com.android.customization.picker.CustomizationPickerActivity"));
+        ResolveInfo ri = launcher.getPackageManager().resolveActivity(intent, 0);
+        return ri != null;
+    }
+
     public static boolean onWidgetsClicked(View view) {
         return openWidgets(Launcher.getLauncher(view.getContext()));
     }
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 1353a23..122151e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -44,6 +44,13 @@
         return LauncherInstrumentation.ContainerType.ALL_APPS;
     }
 
+    private boolean hasClickableIcon(UiObject2 allAppsContainer, BySelector appIconSelector) {
+        final UiObject2 icon = allAppsContainer.findObject(appIconSelector);
+        if (icon == null) return false;
+        final UiObject2 navBar = mLauncher.waitForSystemUiObject("navigation_bar_frame");
+        return icon.getVisibleBounds().bottom < navBar.getVisibleBounds().top;
+    }
+
     /**
      * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
      * sure the icon is visible.
@@ -55,10 +62,10 @@
     public AppIcon getAppIcon(String appName) {
         final UiObject2 allAppsContainer = verifyActiveContainer();
         final BySelector appIconSelector = AppIcon.getAppIconSelector(appName, mLauncher);
-        if (!allAppsContainer.hasObject(appIconSelector)) {
+        if (!hasClickableIcon(allAppsContainer, appIconSelector)) {
             scrollBackToBeginning();
             int attempts = 0;
-            while (!allAppsContainer.hasObject(appIconSelector) &&
+            while (!hasClickableIcon(allAppsContainer, appIconSelector) &&
                     allAppsContainer.scroll(Direction.DOWN, 0.8f)) {
                 LauncherInstrumentation.assertTrue(
                         "Exceeded max scroll attempts: " + MAX_SCROLL_ATTEMPTS,
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
index 2642815..dcc51b5 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsFromOverview.java
@@ -23,6 +23,8 @@
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.UiObject2;
 
+import com.android.launcher3.TestProtocol;
+
 /**
  * Operations on AllApps opened from Overview.
  */
@@ -45,7 +47,11 @@
         final UiObject2 qsb = mLauncher.waitForObjectInContainer(
                 allAppsContainer, "search_container_all_apps");
         final Point start = qsb.getVisibleCenter();
-        final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.6);
+        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);
 
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 3220691..2cde8ec 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -22,8 +22,6 @@
 
 import static org.junit.Assert.assertTrue;
 
-import android.view.ViewConfiguration;
-
 import androidx.annotation.NonNull;
 import androidx.test.uiautomator.By;
 import androidx.test.uiautomator.Until;
@@ -67,11 +65,13 @@
             final int swipeHeight = mLauncher.getTestInfo(
                     getSwipeHeightRequestName()).
                     getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
-            final int slop = ViewConfiguration.get(mLauncher.getContext()).getScaledTouchSlop();
 
-            mLauncher.swipe(centerX, startY, centerX, startY - swipeHeight - slop, expectedState);
+            mLauncher.swipe(
+                    centerX, startY, centerX,
+                    startY - swipeHeight - mLauncher.getTouchSlop(),
+                    expectedState);
         } else {
-            mLauncher.getSystemUiObject("recent_apps").click();
+            mLauncher.waitForSystemUiObject("recent_apps").click();
         }
     }
 
@@ -80,6 +80,6 @@
     }
 
     protected int getSwipeStartY() {
-        return mLauncher.getSystemUiObject("home").getVisibleBounds().centerY();
+        return mLauncher.waitForSystemUiObject("home").getVisibleBounds().centerY();
     }
 }
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 926e470..e3850ff 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 import android.view.MotionEvent;
 import android.view.Surface;
+import android.view.ViewConfiguration;
 import android.view.accessibility.AccessibilityEvent;
 
 import androidx.annotation.NonNull;
@@ -288,7 +289,7 @@
         executeAndWaitForEvent(
                 () -> {
                     log("LauncherInstrumentation.pressHome before clicking");
-                    getSystemUiObject("home").click();
+                    waitForSystemUiObject("home").click();
                 },
                 event -> true,
                 "Pressing Home didn't produce any events");
@@ -298,7 +299,7 @@
         executeAndWaitForEvent(
                 () -> {
                     log("LauncherInstrumentation.pressHome before clicking");
-                    getSystemUiObject("home").click();
+                    waitForSystemUiObject("home").click();
                 },
                 event -> true,
                 "Pressing Home didn't produce any events");
@@ -394,8 +395,9 @@
     }
 
     @NonNull
-    UiObject2 getSystemUiObject(String resId) {
-        final UiObject2 object = mDevice.findObject(By.res(SYSTEMUI_PACKAGE, resId));
+    UiObject2 waitForSystemUiObject(String resId) {
+        final UiObject2 object = mDevice.wait(
+                Until.findObject(By.res(SYSTEMUI_PACKAGE, resId)), WAIT_TIME_MS);
         assertNotNull("Can't find a systemui object with id: " + resId, object);
         return object;
     }
@@ -467,4 +469,8 @@
     float getDisplayDensity() {
         return mInstrumentation.getTargetContext().getResources().getDisplayMetrics().density;
     }
+
+    int getTouchSlop() {
+        return ViewConfiguration.get(getContext()).getScaledTouchSlop();
+    }
 }
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Overview.java b/tests/tapl/com/android/launcher3/tapl/Overview.java
index 0208144..5c8d5eb 100644
--- a/tests/tapl/com/android/launcher3/tapl/Overview.java
+++ b/tests/tapl/com/android/launcher3/tapl/Overview.java
@@ -50,7 +50,7 @@
         verifyActiveContainer();
 
         // Swipe from navbar to the top.
-        final UiObject2 navBar = mLauncher.getSystemUiObject("navigation_bar_frame");
+        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);
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 819e10d..e3ef74a 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -51,15 +51,16 @@
     @NonNull
     public AllApps switchToAllApps() {
         verifyActiveContainer();
-        // Swipe from the hotseat to near the top, e.g. 10% of the screen.
         final UiObject2 hotseat = mHotseat;
         final Point start = hotseat.getVisibleCenter();
-        final int endY = (int) (mLauncher.getDevice().getDisplayHeight() * 0.1f);
+        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,
-                endY,
+                start.y - swipeHeight - mLauncher.getTouchSlop(),
                 ALL_APPS_STATE_ORDINAL
         );