Merge "Dump RecentsOrientationState for bug reports" into ub-launcher3-rvc-dev
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index d7191b4..ff5bf0d 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -156,7 +156,6 @@
         <provider
             android:name="com.android.launcher3.graphics.GridOptionsProvider"
             android:authorities="${packageName}.grid_control"
-            android:enabled="false"
             android:exported="true" />
 
         <!--
diff --git a/protos/launcher_atom.proto b/protos/launcher_atom.proto
index a89fe5c..f0ecba3 100644
--- a/protos/launcher_atom.proto
+++ b/protos/launcher_atom.proto
@@ -23,9 +23,9 @@
 message ItemInfo {
   oneof Item {
     Application application = 1;
-    Task task= 2;
+    Task task = 2;
     Shortcut shortcut = 3;
-    Widget widget = 4;
+    Widget widget = 4;    
   }
   // When used for launch event, stores the global predictive rank
   optional int32 rank = 5;
@@ -34,13 +34,23 @@
   optional bool is_work = 6;
 
   // Item can be child node to parent container or parent containers (nested)
-  oneof Container {
-    WorkspaceContainer workspace = 7;
-    HotseatContainer hotseat = 8;
-    FolderContainer folder = 9;
-  }
+  optional ContainerInfo container_info = 7;
+
   // Stores the origin of the Item
-  optional Origin source = 10;
+  optional Origin source = 8;
+}
+
+// Represents various launcher surface where items are placed.
+message ContainerInfo {
+  oneof Container {
+    WorkspaceContainer workspace = 1;
+    HotseatContainer hotseat = 2;
+    FolderContainer folder = 3;
+    AllAppsContainer all_apps_container = 4;
+  }
+}
+
+message AllAppsContainer {
 }
 
 enum Origin {
@@ -68,8 +78,8 @@
 
 // AppWidgets handled by AppWidgetManager
 message Widget {
-  optional int32 span_x = 1;
-  optional int32 span_y = 2;
+  optional int32 span_x = 1 [default = 1];
+  optional int32 span_y = 2 [default = 1];
   optional int32 app_widget_id = 3;
   optional string package_name = 4; // only populated during snapshot if from workspace
   optional string component_name = 5; // only populated during snapshot if from workspace
@@ -86,9 +96,9 @@
 // Containers
 
 message WorkspaceContainer {
-  optional int32 page_index = 1; // range [-1, l], 0 is the index of the main homescreen
-  optional int32 grid_x = 2;     // [0, m], m varies based on the display density and resolution
-  optional int32 grid_y = 3;     // [0, n], n varies based on the display density and resolution
+  optional int32 page_index = 1 [default = -2]; // range [-1, l], 0 is the index of the main homescreen
+  optional int32 grid_x = 2 [default = -1]; // [0, m], m varies based on the display density and resolution
+  optional int32 grid_y = 3 [default = -1]; // [0, n], n varies based on the display density and resolution
 }
 
 message HotseatContainer {
@@ -96,13 +106,11 @@
 }
 
 message FolderContainer {
-  optional int32 page_index = 1;
-  optional int32 grid_x = 2;
-  optional int32 grid_y = 3;
-  oneof Container {
+  optional int32 page_index = 1 [default = -1];
+  optional int32 grid_x = 2 [default = -1];
+  optional int32 grid_y = 3 [default = -1];
+  oneof ParentContainer {
     WorkspaceContainer workspace = 4;
     HotseatContainer hotseat = 5;
   }
 }
-
-
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 634eb49..ee642db 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -17,6 +17,7 @@
 
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
 
 import android.content.Intent;
@@ -221,7 +222,12 @@
 
         @Override
         protected boolean isRecentsInteractive() {
-            return mActivity.isInState(OVERVIEW);
+            return mActivity.isInState(OVERVIEW) || mActivity.isInState(OVERVIEW_MODAL_TASK);
+        }
+
+        @Override
+        protected boolean isRecentsModal() {
+            return mActivity.isInState(OVERVIEW_MODAL_TASK);
         }
 
         @Override
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 2f55fda..a1cc60e 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
@@ -19,6 +19,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
 import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
+import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
 
 import android.annotation.TargetApi;
 import android.os.Build;
@@ -88,6 +89,11 @@
     }
 
     @Override
+    FloatProperty<RecentsView> getTaskModalnessProperty() {
+        return TASK_MODALNESS;
+    }
+
+    @Override
     FloatProperty<RecentsView> getContentAlphaProperty() {
         return CONTENT_ALPHA;
     }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
new file mode 100644
index 0000000..868273a
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 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 android.content.res.Resources;
+import android.graphics.Rect;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.R;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+import com.android.quickstep.views.RecentsView;
+
+/**
+ * An Overview state that shows the current task in a modal fashion. Modal state is where the
+ * current task is shown on its own without other tasks visible.
+ */
+public class OverviewModalTaskState extends OverviewState {
+
+    private static final int STATE_FLAGS =
+            FLAG_DISABLE_RESTORE | FLAG_OVERVIEW_UI | FLAG_DISABLE_ACCESSIBILITY;
+
+    public OverviewModalTaskState(int id) {
+        super(id, ContainerType.OVERVIEW, STATE_FLAGS);
+    }
+
+    @Override
+    public int getTransitionDuration(Launcher launcher) {
+        return 300;
+    }
+
+    @Override
+    public int getVisibleElements(Launcher launcher) {
+        return OVERVIEW_BUTTONS;
+    }
+
+    @Override
+    public float[] getOverviewScaleAndOffset(Launcher launcher) {
+        Resources res = launcher.getBaseContext().getResources();
+
+        Rect out = new Rect();
+        launcher.<RecentsView>getOverviewPanel().getTaskSize(out);
+        int taskHeight = out.height();
+
+        float topMargin = res.getDimension(R.dimen.task_thumbnail_top_margin);
+        float bottomMargin = res.getDimension(R.dimen.task_thumbnail_bottom_margin_with_actions);
+        float newHeight = taskHeight + topMargin + bottomMargin;
+        float scale = newHeight / taskHeight;
+
+        return new float[] {scale, 0};
+    }
+
+    @Override
+    public float getOverviewModalness() {
+        return 1.0f;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
index e44f59f..fad9ea5 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -259,4 +259,11 @@
     public static OverviewState newSwitchState(int id) {
         return new QuickSwitchState(id);
     }
+
+    /**
+     *  New Overview substate that represents the overview in modal mode (one task shown on its own)
+     */
+    public static OverviewState newModalTaskState(int id) {
+        return new OverviewModalTaskState(id);
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index 2b456ec..06a481b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -17,7 +17,6 @@
 
 import static com.android.launcher3.LauncherState.ALL_APPS;
 import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
 import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
@@ -99,7 +98,7 @@
         if (!cameFromNavBar) {
             return false;
         }
-        if (mStartState == OVERVIEW || mStartState == ALL_APPS) {
+        if (mStartState.overviewUi || mStartState == ALL_APPS) {
             return true;
         }
         if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
@@ -129,7 +128,7 @@
     private void initCurrentAnimation() {
         long accuracy = (long) (getShiftRange() * 2);
         final PendingAnimation builder = new PendingAnimation(accuracy);
-        if (mStartState == OVERVIEW) {
+        if (mStartState.overviewUi) {
             RecentsView recentsView = mLauncher.getOverviewPanel();
             builder.setFloat(recentsView, ADJACENT_PAGE_OFFSET,
                     -mPullbackDistance / recentsView.getPageOffsetScale(), PULLBACK_INTERPOLATOR);
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index f6f892b..1f3b82c 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -96,6 +96,9 @@
 
     protected abstract boolean isRecentsInteractive();
 
+    /** Is recents view showing a single task in a modal way. */
+    protected abstract boolean isRecentsModal();
+
     protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
     }
 
@@ -134,7 +137,7 @@
                     if (mRecentsView.isTaskViewVisible(view) && mActivity.getDragLayer()
                             .isEventOverView(view, ev)) {
                         // Disable swiping up and down if the task overlay is modal.
-                        if (view.isTaskOverlayModal()) {
+                        if (isRecentsModal()) {
                             mTaskBeingDragged = null;
                             break;
                         }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
index 147f933..b44d6df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskOverlayFactory.java
@@ -131,13 +131,6 @@
         }
 
         /**
-         * Whether the overlay is modal, which means only tapping is enabled, but no swiping.
-         */
-        public boolean isOverlayModal() {
-            return false;
-        }
-
-        /**
          * Gets the task snapshot as it is displayed on the screen.
          *
          * @return the bounds of the snapshot in screen coordinates.
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 a113604..d7458d2 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
@@ -28,4 +28,9 @@
     protected boolean isRecentsInteractive() {
         return mActivity.hasWindowFocus();
     }
+
+    @Override
+    protected boolean isRecentsModal() {
+        return false;
+    }
 }
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
index 820bd17..32fc0de 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/StaggeredWorkspaceAnim.java
@@ -202,6 +202,12 @@
         alpha.setInterpolator(LINEAR);
         alpha.setDuration(ALPHA_DURATION_MS);
         alpha.setStartDelay(startDelay);
+        alpha.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                v.setAlpha(1f);
+            }
+        });
         mAnimators.play(alpha);
     }
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
index e455939..1018211 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/ClearAllButton.java
@@ -56,7 +56,8 @@
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
-        mScrollOffset = mIsRtl ? mParent.getPaddingRight() / 2 : - mParent.getPaddingLeft() / 2;
+        PagedOrientationHandler orientationHandler = mParent.getPagedOrientationHandler();
+        mScrollOffset = orientationHandler.getClearAllScrollOffset(mParent, mIsRtl);
     }
 
     @Override
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 0b6d340..9005651 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
@@ -20,6 +20,7 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.LauncherState.OVERVIEW;
 import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
+import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
 import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -284,7 +285,7 @@
             // Clean-up logic that occurs when recents is no longer in use/visible.
             reset();
         }
-        setOverlayEnabled(finalState == OVERVIEW);
+        setOverlayEnabled(finalState == OVERVIEW || finalState == OVERVIEW_MODAL_TASK);
         setFreezeViewVisibility(false);
     }
 
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 6041917..cd3abed 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
@@ -70,7 +70,6 @@
 import android.text.TextPaint;
 import android.util.AttributeSet;
 import android.util.FloatProperty;
-import android.util.Log;
 import android.util.Property;
 import android.util.SparseBooleanArray;
 import android.view.HapticFeedbackConstants;
@@ -173,13 +172,26 @@
                 }
             };
 
+    public static final FloatProperty<RecentsView> TASK_MODALNESS =
+            new FloatProperty<RecentsView>("taskModalness") {
+                @Override
+                public void setValue(RecentsView recentsView, float v) {
+                    recentsView.setTaskModalness(v);
+                }
+
+                @Override
+                public Float get(RecentsView recentsView) {
+                    return recentsView.mTaskModalness;
+                }
+            };
+
     public static final FloatProperty<RecentsView> ADJACENT_PAGE_OFFSET =
             new FloatProperty<RecentsView>("adjacentPageOffset") {
                 @Override
                 public void setValue(RecentsView recentsView, float v) {
                     if (recentsView.mAdjacentPageOffset != v) {
                         recentsView.mAdjacentPageOffset = v;
-                        recentsView.updateAdjacentPageOffset();
+                        recentsView.updatePageOffsets();
                     }
                 }
 
@@ -327,6 +339,12 @@
     protected float mContentAlpha = 1;
     @ViewDebug.ExportedProperty(category = "launcher")
     protected float mFullscreenProgress = 0;
+    /**
+     * How modal is the current task to be displayed, 1 means the task is fully modal and no other
+     * tasks are show. 0 means the task is displays in context in the list with other tasks.
+     */
+    @ViewDebug.ExportedProperty(category = "launcher")
+    protected float mTaskModalness = 0;
 
     // Keeps track of task id whose visual state should not be reset
     private int mIgnoreResetTaskId = -1;
@@ -647,7 +665,7 @@
     @Override
     protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
         // Enables swiping to the left or right only if the task overlay is not modal.
-        if (getCurrentPageTaskView() == null || !getCurrentPageTaskView().isTaskOverlayModal()) {
+        if (mTaskModalness == 0f) {
             super.determineScrollingStart(ev, touchSlopScale);
         }
     }
@@ -735,25 +753,6 @@
         return taskViewCount;
     }
 
-    /**
-     * Updates UI for a modal task, including hiding other tasks.
-     */
-    public void updateUiForModalTask(TaskView taskView, boolean isTaskOverlayModal) {
-        int currentIndex = indexOfChild(taskView);
-        TaskView previousTask = getTaskViewAt(currentIndex - 1);
-        TaskView nextTask = getTaskViewAt(currentIndex + 1);
-        float alpha = isTaskOverlayModal ? 0.0f : 1.0f;
-        if (previousTask != null) {
-            previousTask.animate().alpha(alpha)
-                    .translationX(isTaskOverlayModal ? previousTask.getWidth() / 2 : 0);
-        }
-        if (nextTask != null) {
-            nextTask.animate().alpha(alpha)
-                    .translationX(isTaskOverlayModal ? -nextTask.getWidth() / 2 : 0);
-
-        }
-    }
-
     protected void onTaskStackUpdated() { }
 
     public void resetTaskVisuals() {
@@ -776,6 +775,7 @@
         updateCurveProperties();
         // Update the set of visible task's data
         loadVisibleTaskData();
+        setTaskModalness(0);
     }
 
     public void setFullscreenProgress(float fullscreenProgress) {
@@ -1653,21 +1653,27 @@
                 mTempRect, mActivity.getDeviceProfile(), mTempPointF);
         setPivotX(mTempPointF.x);
         setPivotY(mTempPointF.y);
-        updateAdjacentPageOffset();
+        updatePageOffsets();
     }
 
-    private void updateAdjacentPageOffset() {
+    private void updatePageOffsets() {
         float offset = mAdjacentPageOffset * getWidth();
+        float modalOffset = mTaskModalness * getWidth();
         if (mIsRtl) {
             offset = -offset;
+            modalOffset = -modalOffset;
         }
         int count = getChildCount();
 
         TaskView runningTask = mRunningTaskId == -1 ? null : getTaskView(mRunningTaskId);
         int midPoint = runningTask == null ? -1 : indexOfChild(runningTask);
+        int currentPage = getCurrentPage();
 
         for (int i = 0; i < count; i++) {
-            getChildAt(i).setTranslationX(i == midPoint ? 0 : (i < midPoint ? -offset : offset));
+            float translation = i == midPoint ? 0 : (i < midPoint ? -offset : offset);
+            float modalTranslation =
+                    i == currentPage ? 0 : (i < currentPage ? -modalOffset : modalOffset);
+            getChildAt(i).setTranslationX(translation + modalTranslation);
         }
         updateCurveProperties();
     }
@@ -2113,6 +2119,18 @@
         }
     }
 
+    /**
+     * The current task is fully modal (modalness = 1) when it is shown on its own in a modal
+     * way. Modalness 0 means the task is shown in context with all the other tasks.
+     */
+    private void setTaskModalness(float modalness) {
+        mTaskModalness = modalness;
+        updatePageOffsets();
+        if (getCurrentPageTaskView() != null) {
+            getCurrentPageTaskView().setModalness(modalness);
+        }
+    }
+
     @Nullable
     protected DepthController getDepthController() {
         return null;
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 4275933..aea5b8e 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
@@ -25,6 +25,7 @@
 import static android.widget.Toast.LENGTH_SHORT;
 
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
+import static com.android.launcher3.Utilities.comp;
 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
@@ -165,10 +166,10 @@
     private ObjectAnimator mIconAndDimAnimator;
     private float mIconScaleAnimStartProgress = 0;
     private float mFocusTransitionProgress = 1;
+    private float mModalness = 0;
     private float mStableAlpha = 1;
 
     private boolean mShowScreenshot;
-    private boolean mRunningModalAnimation = false;
 
     // The current background requests to load the task thumbnail and icon
     private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
@@ -239,59 +240,24 @@
         mIconView = findViewById(R.id.icon);
     }
 
-    public boolean isTaskOverlayModal() {
-        return mSnapshotView.getTaskOverlay().isOverlayModal();
-    }
-
-    /** Updates UI based on whether the task is modal. */
-    public void updateUiForModalTask() {
-        boolean isOverlayModal = isTaskOverlayModal();
-        mRunningModalAnimation = true;
-        if (getRecentsView() != null) {
-            getRecentsView().updateUiForModalTask(this, isOverlayModal);
+    /**
+     * The modalness of this view is how it should be displayed when it is shown on its own in the
+     * modal state of overview.
+     *
+     * @param modalness [0, 1] 0 being in context with other tasks, 1 being shown on its own.
+     */
+    public void setModalness(float modalness) {
+        mModalness = modalness;
+        mIconView.setAlpha(comp(modalness));
+        if (mContextualChip != null) {
+            mContextualChip.setScaleX(comp(modalness));
+            mContextualChip.setScaleY(comp(modalness));
+        }
+        if (mContextualChipWrapper != null) {
+            mContextualChipWrapper.setAlpha(comp(modalness));
         }
 
-        // Hides footers and icon when overlay is modal.
-        if (isOverlayModal) {
-            for (FooterWrapper footer : mFooters) {
-                if (footer != null) {
-                    footer.animateHide();
-                }
-            }
-            if (mContextualChipWrapper != null) {
-                mContextualChipWrapper.animate().alpha(0f).setDuration(300);
-            }
-            if (mContextualChip != null) {
-                mContextualChip.animate().scaleX(0f).scaleY(0f).setDuration(300);
-            }
-
-            mIconView.animate().alpha(0.0f);
-        } else {
-            if (mContextualChip != null) {
-                mContextualChip.animate().scaleX(1f).scaleY(1f).setDuration(300);
-            }
-            if (mContextualChipWrapper != null) {
-                mContextualChipWrapper.animate().alpha(1f).setDuration(300);
-            }
-            mIconView.animate().alpha(1.0f);
-        }
-
-        // Sets animations for modal UI. We will remove the margins to zoom in the snapshot.
-        float topMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin);
-        float bottomMargin =
-                getResources().getDimension(R.dimen.task_thumbnail_bottom_margin_with_actions);
-        float newHeight = mSnapshotView.getHeight() + topMargin + bottomMargin;
-        float scale = isOverlayModal ? newHeight / mSnapshotView.getHeight() : 1.0f;
-        float centerDifference = (bottomMargin - topMargin) / 2;
-        float translationY = isOverlayModal ? centerDifference : 0;
-        this.animate().scaleX(scale).scaleY(scale).translationY(translationY)
-                .withEndAction(new Runnable() {
-                    @Override
-                    public void run() {
-                        setCurveScale(scale);
-                        mRunningModalAnimation = false;
-                    }
-                });
+        updateFooterVerticalOffset(mFooterVerticalOffset);
     }
 
     public TaskMenuView getMenuView() {
@@ -535,12 +501,7 @@
         mIconView.setScaleX(scale);
         mIconView.setScaleY(scale);
 
-        mFooterVerticalOffset = 1.0f - scale;
-        for (FooterWrapper footer : mFooters) {
-            if (footer != null) {
-                footer.updateFooterOffset();
-            }
-        }
+        updateFooterVerticalOffset(1.0f - scale);
     }
 
     public void setIconScaleAnimStartProgress(float startProgress) {
@@ -586,6 +547,7 @@
     public void resetVisualProperties() {
         resetViewTransforms();
         setFullscreenProgress(0);
+        setModalness(0);
     }
 
     public void setStableAlpha(float parentAlpha) {
@@ -606,7 +568,7 @@
     @Override
     public void onPageScroll(ScrollState scrollState) {
         // Don't do anything if it's modal.
-        if (mRunningModalAnimation || isTaskOverlayModal()) {
+        if (mModalness > 0) {
             return;
         }
 
@@ -759,6 +721,12 @@
                 mStackHeight += footer.mView.getHeight();
             }
         }
+        updateFooterVerticalOffset(0);
+    }
+
+    private void updateFooterVerticalOffset(float offset) {
+        mFooterVerticalOffset = offset;
+
         for (FooterWrapper footer : mFooters) {
             if (footer != null) {
                 footer.updateFooterOffset();
@@ -857,7 +825,8 @@
         }
 
         void updateFooterOffset() {
-            mAnimationOffset = Math.round(mStackHeight * mFooterVerticalOffset);
+            float offset = Utilities.or(mFooterVerticalOffset, mModalness);
+            mAnimationOffset = Math.round(mStackHeight * offset);
             mView.setTranslationY(mAnimationOffset + mEntryAnimationOffset
                     + mCurrentFullscreenParams.mCurrentDrawnInsets.bottom
                     + mCurrentFullscreenParams.mCurrentDrawnInsets.top);
@@ -880,22 +849,6 @@
             animator.setDuration(100);
             animator.start();
         }
-
-        void animateHide() {
-            ValueAnimator animator = ValueAnimator.ofFloat(0.0f, 1.0f);
-            animator.addUpdateListener(anim -> {
-                mFooterVerticalOffset = anim.getAnimatedFraction();
-                updateFooterOffset();
-            });
-            animator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    removeView(mView);
-                }
-            });
-            animator.setDuration(100);
-            animator.start();
-        }
     }
 
     private int getExpectedViewHeight(View view) {
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 1cb0aa4..e718598 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -493,7 +493,7 @@
                 : APP_LAUNCH_ALPHA_DOWN_DURATION;
 
         RectF targetBounds = new RectF(windowTargetBounds);
-        RectF currentBounds = new RectF();
+        RectF iconBounds = new RectF();
         RectF temp = new RectF();
         Point tmpPos = new Point();
 
@@ -531,7 +531,7 @@
         appAnimator.addUpdateListener(new MultiValueUpdateListener() {
             FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
             FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
-            FloatProp mIconScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
+            FloatProp mScale = new FloatProp(initialStartScale, scale, 0, APP_LAUNCH_DURATION,
                     EXAGGERATED_EASE);
             FloatProp mIconAlpha = new FloatProp(1f, 0f, APP_LAUNCH_ALPHA_START_DELAY,
                     alphaDuration, LINEAR);
@@ -542,40 +542,48 @@
 
             @Override
             public void onUpdate(float percent) {
-                // Calculate app icon size.
-                float iconWidth = bounds.width() * mIconScale.value;
-                float iconHeight = bounds.height() * mIconScale.value;
+                // Calculate the size.
+                float width = bounds.width() * mScale.value;
+                float height = bounds.height() * mScale.value;
 
-                // Animate the window crop so that it starts off as a square.
-                final int windowWidth;
-                final int windowHeight;
+                // Animate the crop so that it starts off as a square.
+                final int cropWidth;
+                final int cropHeight;
                 if (mDeviceProfile.isVerticalBarLayout()) {
-                    windowWidth = (int) mCroppedSize.value;
-                    windowHeight = windowTargetBounds.height();
+                    cropWidth = (int) mCroppedSize.value;
+                    cropHeight = windowTargetBounds.height();
                 } else {
-                    windowWidth = windowTargetBounds.width();
-                    windowHeight = (int) mCroppedSize.value;
+                    cropWidth = windowTargetBounds.width();
+                    cropHeight = (int) mCroppedSize.value;
                 }
-                crop.set(0, 0, windowWidth, windowHeight);
+                crop.set(0, 0, cropWidth, cropHeight);
 
-                // Scale the app window to match the icon size.
-                float scaleX = iconWidth / windowWidth;
-                float scaleY = iconHeight / windowHeight;
+                // Scale the size to match the crop.
+                float scaleX = width / cropWidth;
+                float scaleY = height / cropHeight;
                 float scale = Math.min(1f, Math.max(scaleX, scaleY));
 
-                float scaledWindowWidth = windowWidth * scale;
-                float scaledWindowHeight = windowHeight * scale;
+                float scaledCropWidth = cropWidth * scale;
+                float scaledCropHeight = cropHeight * scale;
+                float offsetX  = (scaledCropWidth - width) / 2;
+                float offsetY = (scaledCropHeight - height) / 2;
 
-                float offsetX = (scaledWindowWidth - iconWidth) / 2;
-                float offsetY = (scaledWindowHeight - iconHeight) / 2;
-
-                // Calculate the window position
+                // Calculate the window position.
                 temp.set(bounds);
                 temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
                 temp.offset(mDx.value, mDy.value);
-                Utilities.scaleRectFAboutCenter(temp, mIconScale.value);
-                float transX0 = temp.left - offsetX;
-                float transY0 = temp.top - offsetY;
+                Utilities.scaleRectFAboutCenter(temp, mScale.value);
+                float windowTransX0 = temp.left - offsetX;
+                float windowTransY0 = temp.top - offsetY;
+
+                // Calculate the icon position.
+                iconBounds.set(bounds);
+                iconBounds.offset(mDx.value, mDy.value);
+                Utilities.scaleRectFAboutCenter(iconBounds, mScale.value);
+                iconBounds.left -= offsetX;
+                iconBounds.top -= offsetY;
+                iconBounds.right += offsetX;
+                iconBounds.bottom += offsetY;
 
                 float croppedHeight = (windowTargetBounds.height() - crop.height()) * scale;
                 float croppedWidth = (windowTargetBounds.width() - crop.width()) * scale;
@@ -584,28 +592,23 @@
                     RemoteAnimationTargetCompat target = appTargets[i];
                     SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
 
-                    tmpPos.set(target.position.x, target.position.y);
-                    if (target.localBounds != null) {
-                        final Rect localBounds = target.localBounds;
-                        tmpPos.set(target.localBounds.left, target.localBounds.top);
-                    }
-
                     if (target.mode == MODE_OPENING) {
                         matrix.setScale(scale, scale);
-                        matrix.postTranslate(transX0, transY0);
-                        matrix.mapRect(currentBounds, targetBounds);
-                        if (mDeviceProfile.isVerticalBarLayout()) {
-                            currentBounds.right -= croppedWidth;
-                        } else {
-                            currentBounds.bottom -= croppedHeight;
-                        }
-                        floatingView.update(currentBounds, mIconAlpha.value, percent, 0f,
+                        matrix.postTranslate(windowTransX0, windowTransY0);
+
+                        floatingView.update(iconBounds, mIconAlpha.value, percent, 0f,
                                 mWindowRadius.value * scale, true /* isOpening */);
                         builder.withMatrix(matrix)
                                 .withWindowCrop(crop)
                                 .withAlpha(1f - mIconAlpha.value)
                                 .withCornerRadius(mWindowRadius.value);
                     } else {
+                        tmpPos.set(target.position.x, target.position.y);
+                        if (target.localBounds != null) {
+                            final Rect localBounds = target.localBounds;
+                            tmpPos.set(target.localBounds.left, target.localBounds.top);
+                        }
+
                         matrix.setTranslate(tmpPos.x, tmpPos.y);
                         builder.withMatrix(matrix)
                                 .withWindowCrop(target.screenSpaceBounds)
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index 33011ac..ac50d6d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -21,6 +21,7 @@
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 import static com.android.launcher3.graphics.Scrim.SCRIM_PROGRESS;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
+import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_MODAL;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCRIM_FADE;
 import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TRANSLATE_X;
@@ -66,6 +67,7 @@
         getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
         OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
         SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
+        getTaskModalnessProperty().set(mRecentsView, state.getOverviewModalness());
     }
 
     @Override
@@ -101,8 +103,15 @@
         OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
         setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
                 config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
+
+        setter.setFloat(
+                mRecentsView, getTaskModalnessProperty(),
+                toState.getOverviewModalness(),
+                config.getInterpolator(ANIM_OVERVIEW_MODAL, AGGRESSIVE_EASE_IN_OUT));
     }
 
+    abstract FloatProperty getTaskModalnessProperty();
+
     /**
      * Get property for content alpha for the recents view.
      *
diff --git a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
index 2a9f32d..879fd1d 100644
--- a/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
+++ b/quickstep/src/com/android/quickstep/OrientationTouchTransformer.java
@@ -55,6 +55,8 @@
     private static final boolean DEBUG = false;
     private static final int MAX_ORIENTATIONS = 4;
 
+    private static final int QUICKSTEP_ROTATION_UNINITIALIZED = -1;
+
     private final Matrix mTmpMatrix = new Matrix();
     private final float[] mTmpPoint = new float[2];
 
@@ -69,9 +71,10 @@
     private QuickStepContractInfo mContractInfo;
 
     /**
-     * Represents if we're currently in a swipe "session" of sorts. If value is -1, then user
-     * has not tapped on an active nav region. Otherwise it will be the rotation of the display
-     * when the user first interacted with the active nav bar region.
+     * Represents if we're currently in a swipe "session" of sorts. If value is
+     * QUICKSTEP_ROTATION_UNINITIALIZED, then user has not tapped on an active nav region.
+     * Otherwise it will be the rotation of the display when the user first interacted with the
+     * active nav bar region.
      * The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is
      * called - usually from a timeout or if user starts interacting w/ the foreground app.
      *
@@ -79,7 +82,7 @@
      * the rect is purely used for tracking touch interactions and usually this "session" will
      * outlast the touch interaction.
      */
-    private int mQuickStepStartingRotation = -1;
+    private int mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
 
     /** For testability */
     interface QuickStepContractInfo {
@@ -116,7 +119,7 @@
      */
     void createOrAddTouchRegion(DefaultDisplay.Info info) {
         mCurrentDisplayRotation = info.rotation;
-        if (mQuickStepStartingRotation > -1
+        if (mQuickStepStartingRotation > QUICKSTEP_ROTATION_UNINITIALIZED
                 && mCurrentDisplayRotation == mQuickStepStartingRotation) {
             // User already was swiping and the current screen is same rotation as the starting one
             // Remove active nav bars in other rotations except for the one we started out in
@@ -146,7 +149,7 @@
         mEnableMultipleRegions = enableMultipleRegions &&
                 mMode != SysUINavigationMode.Mode.TWO_BUTTONS;
         if (!enableMultipleRegions) {
-            mQuickStepStartingRotation = -1;
+            mQuickStepStartingRotation = QUICKSTEP_ROTATION_UNINITIALIZED;
             resetSwipeRegions(info);
         }
     }
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index a6ce2b5..ef14e28 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -511,14 +511,12 @@
 
     void enableMultipleRegions(boolean enable) {
         mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
-        if (enable) {
-            UI_HELPER_EXECUTOR.execute(() -> {
-                int quickStepStartingRotation =
-                        mOrientationTouchTransformer.getQuickStepStartingRotation();
-                SystemUiProxy.INSTANCE.get(mContext)
-                        .onQuickSwitchToNewTask(quickStepStartingRotation);
-            });
-        }
+        UI_HELPER_EXECUTOR.execute(() -> {
+            int quickStepStartingRotation =
+                    mOrientationTouchTransformer.getQuickStepStartingRotation();
+            SystemUiProxy.INSTANCE.get(mContext)
+                    .onQuickSwitchToNewTask(quickStepStartingRotation);
+        });
     }
 
     public int getCurrentActiveRotation() {
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index d72f3bb..c8b2876 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -80,7 +80,7 @@
     <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
     <string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
     <string name="styles_wallpaper_button_text" msgid="4342122323125579619">"Styles et fonds d\'écran"</string>
-    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'écran d\'accueil"</string>
+    <string name="settings_button_text" msgid="8873672322605444408">"Paramètres d\'accueil"</string>
     <string name="msg_disabled_by_admin" msgid="6898038085516271325">"Désactivé par votre administrateur"</string>
     <string name="allow_rotation_title" msgid="7728578836261442095">"Autoriser la rotation de l\'écran d\'accueil"</string>
     <string name="allow_rotation_desc" msgid="8662546029078692509">"Lorsque vous faites pivoter le téléphone"</string>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index f69248c..3fa16ab 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -139,8 +139,7 @@
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"बुझेँ"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको ब्याट्री प्रयोग गर्न वा तपाईंको स्थानसम्बन्धी जानकारीमाथि पहुँच राख्न सक्दैनन्"</string>
-    <!-- no translation found for work_apps_paused_content_description (7553586952985486433) -->
-    <skip />
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"कार्यालयको प्रोफाइल अस्थायी रूपमा रोक्का गरिएको छ। कामसम्बन्धी एपहरूले तपाईंलाई सूचना पठाउन, तपाईंको यन्त्रको ब्याट्री प्रयोग गर्न वा तपाईंको स्थान हेर्न सक्दैनन्"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"कामसम्बन्धी एप र सूचनाहरू अस्थायी रूपमा रोक्का गर्नुहोस्"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"कार्य पूरा गर्न सकिएन: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml
index b2cf426..e548b87 100644
--- a/res/values-or/strings.xml
+++ b/res/values-or/strings.xml
@@ -139,7 +139,7 @@
     <string name="work_profile_edu_accept" msgid="6069788082535149071">"ବୁଝିଗଲି"</string>
     <string name="work_apps_paused_title" msgid="2389865654362803723">"ୱାର୍କ ପ୍ରୋଫାଇଲକୁ ବିରତ କରାଯାଇଛି"</string>
     <string name="work_apps_paused_body" msgid="5388070126389079077">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀକୁ ବ୍ୟବହାର କରିପାରିବ ନାହିଁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ ନାହିଁ"</string>
-    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ବିରତ କରାଯାଇଛି। କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
+    <string name="work_apps_paused_content_description" msgid="7553586952985486433">"ୱାର୍କ ପ୍ରୋଫାଇଲ୍ ବିରତ କରାଯାଇଛି। କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଆପଣଙ୍କୁ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ପଠାଇପାରିବ ନାହିଁ, ଆପଣଙ୍କ ବ୍ୟାଟେରୀ ବ୍ୟବହାର କରନ୍ତୁ କିମ୍ବା ଆପଣଙ୍କ ଲୋକେସନ୍ ଆକ୍ସେସ୍ କରନ୍ତୁ"</string>
     <string name="work_switch_tip" msgid="808075064383839144">"କାର୍ଯ୍ୟସ୍ଥଳୀ ଆପ୍ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ବିରତ କରନ୍ତୁ"</string>
     <string name="remote_action_failed" msgid="1383965239183576790">"ବିଫଳ ହୋଇଛି: <xliff:g id="WHAT">%1$s</xliff:g>"</string>
 </resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 41bb909..4f21315 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -132,7 +132,7 @@
     <item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">800</item>
 
     <item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.8</item>
-    <item name="horizontal_spring_stiffness" type="dimen" format="float">400</item>
+    <item name="horizontal_spring_stiffness" type="dimen" format="float">250</item>
 
     <item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
     <item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index 63b90ae..8951674 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -19,7 +19,6 @@
 import static com.android.launcher3.Utilities.getDevicePrefs;
 import static com.android.launcher3.Utilities.getPointString;
 import static com.android.launcher3.config.FeatureFlags.APPLY_CONFIG_AT_RUNTIME;
-import static com.android.launcher3.settings.SettingsActivity.GRID_OPTIONS_PREFERENCE_KEY;
 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
 
@@ -29,7 +28,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -216,9 +214,8 @@
     }
 
     public static String getCurrentGridName(Context context) {
-        SharedPreferences prefs = Utilities.getPrefs(context);
-        return prefs.getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
-                ? prefs.getString(KEY_IDP_GRID_NAME, null) : null;
+        return Utilities.isGridOptionsEnabled(context)
+                ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null) : null;
     }
 
     /**
@@ -263,7 +260,7 @@
         iconTextSize = displayOption.iconTextSize;
         fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
 
-        if (Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
+        if (Utilities.isGridOptionsEnabled(context)) {
             allAppsIconSize = displayOption.allAppsIconSize;
             allAppsIconTextSize = displayOption.allAppsIconTextSize;
         } else {
@@ -344,9 +341,7 @@
         InvariantDeviceProfile oldProfile = new InvariantDeviceProfile(this);
 
         // Re-init grid
-        String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
-                ? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
-                : null;
+        String gridName = getCurrentGridName(context);
         initGrid(context, gridName);
 
         int changeFlags = 0;
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 54d8f0d..e2b867e 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -36,6 +36,7 @@
 import static com.android.launcher3.testing.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.HINT_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
+import static com.android.launcher3.testing.TestProtocol.OVERVIEW_MODAL_TASK_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_PEEK_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
 import static com.android.launcher3.testing.TestProtocol.QUICK_SWITCH_STATE_ORDINAL;
@@ -101,7 +102,7 @@
                 }
             };
 
-    private static final LauncherState[] sAllStates = new LauncherState[8];
+    private static final LauncherState[] sAllStates = new LauncherState[9];
 
     /**
      * TODO: Create a separate class for NORMAL state.
@@ -128,6 +129,8 @@
     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 OVERVIEW_MODAL_TASK = OverviewState.newModalTaskState(
+            OVERVIEW_MODAL_TASK_STATE_ORDINAL);
     public static final LauncherState QUICK_SWITCH =
             OverviewState.newSwitchState(QUICK_SWITCH_STATE_ORDINAL);
     public static final LauncherState BACKGROUND_APP =
@@ -280,6 +283,14 @@
     }
 
     /**
+     * For this state, how modal should over view been shown. 0 modalness means all tasks drawn,
+     * 1 modalness means the current task is show on its own.
+     */
+    public float getOverviewModalness() {
+        return 0;
+    }
+
+    /**
      * The amount of blur and wallpaper zoom to apply to the background of either the app
      * or Launcher surface in this state. Should be a number between 0 and 1, inclusive.
      *
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 3c3ab6c..7baee95 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -24,10 +24,13 @@
 import android.app.Person;
 import android.app.WallpaperManager;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.res.Resources;
@@ -62,6 +65,7 @@
 
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
+import com.android.launcher3.graphics.GridOptionsProvider;
 import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.icons.IconProvider;
 import com.android.launcher3.icons.LauncherIcons;
@@ -76,6 +80,7 @@
 import com.android.launcher3.widget.PendingAddShortcutInfo;
 
 import java.lang.reflect.Method;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 import java.util.regex.Matcher;
@@ -340,6 +345,30 @@
     }
 
     /**
+     * Bounds parameter to the range [0, 1]
+     */
+    public static float saturate(float a) {
+        return boundToRange(a, 0, 1.0f);
+    }
+
+    /**
+     * Returns the compliment (1 - a) of the parameter.
+     */
+    public static float comp(float a) {
+        return 1 - a;
+    }
+
+    /**
+     * Returns the "probabilistic or" of a and b. (a + b - ab).
+     * Useful beyond probability, can be used to combine two unit progresses for example.
+     */
+    public static float or(float a, float b) {
+        float satA = saturate(a);
+        float satB = saturate(b);
+        return satA + satB - (satA * satB);
+    }
+
+    /**
      * Trims the string, removing all whitespace at the beginning and end of the string.
      * Non-breaking whitespaces are also removed.
      */
@@ -485,6 +514,42 @@
                 || e.getCause() instanceof DeadObjectException;
     }
 
+    public static boolean isGridOptionsEnabled(Context context) {
+        return isComponentEnabled(context.getPackageManager(),
+                context.getPackageName(),
+                GridOptionsProvider.class.getName());
+    }
+
+    private static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName) {
+        ComponentName componentName = new ComponentName(pkgName, clsName);
+        int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);
+
+        switch (componentEnabledSetting) {
+            case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+                return false;
+            case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
+                return true;
+            case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
+            default:
+                // We need to get the application info to get the component's default state
+                try {
+                    PackageInfo packageInfo = pm.getPackageInfo(pkgName,
+                            PackageManager.GET_PROVIDERS | PackageManager.GET_DISABLED_COMPONENTS);
+
+                    if (packageInfo.providers != null) {
+                        return Arrays.stream(packageInfo.providers).anyMatch(
+                                pi -> pi.name.equals(clsName) && pi.isEnabled());
+                    }
+
+                    // the component is not declared in the AndroidManifest
+                    return false;
+                } catch (PackageManager.NameNotFoundException e) {
+                    // the package isn't installed on the device
+                    return false;
+                }
+        }
+    }
+
     /**
      * Utility method to post a runnable on the handler, skipping the synchronization barriers.
      */
diff --git a/src/com/android/launcher3/anim/SpringAnimationBuilder.java b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
index bc77aab..770df03 100644
--- a/src/com/android/launcher3/anim/SpringAnimationBuilder.java
+++ b/src/com/android/launcher3/anim/SpringAnimationBuilder.java
@@ -17,6 +17,8 @@
 
 import static com.android.launcher3.anim.Interpolators.LINEAR;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
 import android.content.Context;
 import android.util.FloatProperty;
@@ -195,6 +197,12 @@
         animator.setDuration(getDuration()).setInterpolator(LINEAR);
         animator.addUpdateListener(anim ->
                 property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
+        animator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                property.set(target, mEndValue);
+            }
+        });
         return animator;
     }
 
diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java
index 14f9a3e..7ee2090 100644
--- a/src/com/android/launcher3/model/data/ItemInfo.java
+++ b/src/com/android/launcher3/model/data/ItemInfo.java
@@ -35,6 +35,7 @@
 import com.android.launcher3.LauncherSettings.Favorites;
 import com.android.launcher3.Workspace;
 import com.android.launcher3.logger.LauncherAtom;
+import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
 import com.android.launcher3.util.ContentWriter;
 
 import java.util.Optional;
@@ -290,18 +291,21 @@
                             .setGridX(fInfo.cellX).setGridY(fInfo.cellY));
                     break;
             }
-            itemBuilder.setFolder(folderBuilder);
+            itemBuilder.setContainerInfo(ContainerInfo.newBuilder().setFolder(folderBuilder));
         } else {
             switch (container) {
                 case CONTAINER_HOTSEAT:
-                    itemBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
-                            .setIndex(screenId));
+                    itemBuilder.setContainerInfo(
+                            ContainerInfo.newBuilder().setHotseat(
+                                    LauncherAtom.HotseatContainer.newBuilder().setIndex(screenId)));
                     break;
                 case CONTAINER_DESKTOP:
-                    itemBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
-                            .setGridX(cellX)
-                            .setGridY(cellY)
-                            .setPageIndex(screenId));
+                    itemBuilder.setContainerInfo(
+                            ContainerInfo.newBuilder().setWorkspace(
+                                    LauncherAtom.WorkspaceContainer.newBuilder()
+                                            .setGridX(cellX)
+                                            .setGridY(cellY)
+                                            .setPageIndex(screenId)));
                     break;
             }
         }
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 12085c8..d3213a1 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -23,10 +23,7 @@
 import static com.android.launcher3.states.RotationHelper.getAllowRotationDefaultValue;
 import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver;
 
-import android.content.ComponentName;
-import android.content.Context;
 import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -48,7 +45,6 @@
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
 import com.android.launcher3.config.FeatureFlags;
-import com.android.launcher3.graphics.GridOptionsProvider;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 import com.android.launcher3.util.SecureSettingsObserver;
 
@@ -71,8 +67,6 @@
     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
     public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
-    public static final String GRID_OPTIONS_PREFERENCE_KEY = "pref_grid_options";
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -95,26 +89,7 @@
     }
 
     @Override
-    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-        if (GRID_OPTIONS_PREFERENCE_KEY.equals(key)) {
-
-            final ComponentName cn = new ComponentName(getApplicationContext(),
-                    GridOptionsProvider.class);
-            Context c = getApplicationContext();
-            int oldValue = c.getPackageManager().getComponentEnabledSetting(cn);
-            int newValue;
-            if (Utilities.getPrefs(c).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
-                newValue = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-            } else {
-                newValue = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
-            }
-
-            if (oldValue != newValue) {
-                c.getPackageManager().setComponentEnabledSetting(cn, newValue,
-                        PackageManager.DONT_KILL_APP);
-            }
-        }
-    }
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { }
 
     private boolean startFragment(String fragment, Bundle args, String key) {
         if (Utilities.ATLEAST_P && getSupportFragmentManager().isStateSaved()) {
@@ -233,10 +208,6 @@
                     // Show if plugins are enabled or flag UI is enabled.
                     return FeatureFlags.showFlagTogglerUi(getContext()) ||
                             PluginManagerWrapper.hasPlugins(getContext());
-                case GRID_OPTIONS_PREFERENCE_KEY:
-                    return Utilities.isDevelopersOptionsEnabled(getContext()) &&
-                            Utilities.IS_DEBUG_DEVICE &&
-                            Utilities.existsStyleWallpapers(getContext());
             }
 
             return true;
diff --git a/src/com/android/launcher3/states/StateAnimationConfig.java b/src/com/android/launcher3/states/StateAnimationConfig.java
index 8dccbd3..1c49867 100644
--- a/src/com/android/launcher3/states/StateAnimationConfig.java
+++ b/src/com/android/launcher3/states/StateAnimationConfig.java
@@ -69,6 +69,7 @@
             ANIM_ALL_APPS_FADE,
             ANIM_OVERVIEW_SCRIM_FADE,
             ANIM_ALL_APPS_HEADER_FADE,
+            ANIM_OVERVIEW_MODAL
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AnimType {}
@@ -85,8 +86,9 @@
     public static final int ANIM_ALL_APPS_FADE = 10;
     public static final int ANIM_OVERVIEW_SCRIM_FADE = 11;
     public static final int ANIM_ALL_APPS_HEADER_FADE = 12; // e.g. predictions
+    public static final int ANIM_OVERVIEW_MODAL = 13;
 
-    private static final int ANIM_TYPES_COUNT = 13;
+    private static final int ANIM_TYPES_COUNT = 14;
 
     private final Interpolator[] mInterpolators = new Interpolator[ANIM_TYPES_COUNT];
 
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index a5a06b4..fba6269 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -28,10 +28,11 @@
     public static final int SPRING_LOADED_STATE_ORDINAL = 1;
     public static final int OVERVIEW_STATE_ORDINAL = 2;
     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 int HINT_STATE_ORDINAL = 7;
+    public static final int OVERVIEW_MODAL_TASK_STATE_ORDINAL = 4;
+    public static final int QUICK_SWITCH_STATE_ORDINAL = 5;
+    public static final int ALL_APPS_STATE_ORDINAL = 6;
+    public static final int BACKGROUND_APP_STATE_ORDINAL = 7;
+    public static final int HINT_STATE_ORDINAL = 8;
     public static final String TAPL_EVENTS_TAG = "TaplEvents";
     public static final String SEQUENCE_MAIN = "Main";
     public static final String SEQUENCE_TIS = "TIS";
@@ -47,6 +48,8 @@
                 return "Overview";
             case OVERVIEW_PEEK_STATE_ORDINAL:
                 return "OverviewPeek";
+            case OVERVIEW_MODAL_TASK_STATE_ORDINAL:
+                return "OverviewModalState";
             case QUICK_SWITCH_STATE_ORDINAL:
                 return "QuickSwitch";
             case ALL_APPS_STATE_ORDINAL:
diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
index dc50053..86d3c61 100644
--- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java
@@ -126,6 +126,11 @@
     }
 
     @Override
+    public int getClearAllScrollOffset(View view, boolean isRtl) {
+        return (isRtl ? view.getPaddingBottom() : - view.getPaddingTop()) / 2;
+    }
+
+    @Override
     public int getSecondaryDimension(View view) {
         return view.getWidth();
     }
diff --git a/src/com/android/launcher3/touch/PagedOrientationHandler.java b/src/com/android/launcher3/touch/PagedOrientationHandler.java
index cc15f99..02a020f 100644
--- a/src/com/android/launcher3/touch/PagedOrientationHandler.java
+++ b/src/com/android/launcher3/touch/PagedOrientationHandler.java
@@ -62,6 +62,7 @@
     float getPrimaryVelocity(VelocityTracker velocityTracker, int pointerId);
     int getMeasuredSize(View view);
     float getPrimarySize(RectF rect);
+    int getClearAllScrollOffset(View view, boolean isRtl);
     int getSecondaryDimension(View view);
     FloatProperty<View> getPrimaryViewTranslate();
     FloatProperty<View> getSecondaryViewTranslate();
diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
index 7c30e29..b253e7d 100644
--- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
+++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java
@@ -123,6 +123,11 @@
     }
 
     @Override
+    public int getClearAllScrollOffset(View view, boolean isRtl) {
+        return (isRtl ? view.getPaddingRight() : - view.getPaddingLeft()) / 2;
+    }
+
+    @Override
     public int getSecondaryDimension(View view) {
         return view.getHeight();
     }
diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
index 7beb7f7..e86ec3b 100644
--- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
+++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java
@@ -75,4 +75,15 @@
     public float getTaskMenuY(float y, View thumbnailView) {
         return y + thumbnailView.getMeasuredHeight();
     }
+
+    @Override
+    public int getClearAllScrollOffset(View view, boolean isRtl) {
+        return (isRtl ? view.getPaddingTop() : - view.getPaddingBottom()) / 2;
+    }
+
+    @Override
+    public void setPrimaryAndResetSecondaryTranslate(View view, float translation) {
+        view.setTranslationX(0);
+        view.setTranslationY(-translation);
+    }
 }
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
index e20b2ca..507ff59 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -44,4 +44,11 @@
     public static OverviewState newSwitchState(int id) {
         return new OverviewState(id);
     }
+
+    /**
+     *  New Overview substate that represents the overview in modal mode (one task shown on its own)
+     */
+    public static OverviewState newModalTaskState(int id) {
+        return new OverviewState(id);
+    }
 }
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 9c8e278..160daef 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -101,7 +101,7 @@
     private static String sStrictmodeDetectedActivityLeak;
     private static boolean sActivityLeakReported;
     private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
-    private static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker();
+    protected static final ActivityLeakTracker ACTIVITY_LEAK_TRACKER = new ActivityLeakTracker();
 
     protected LooperExecutor mMainThreadExecutor = MAIN_EXECUTOR;
     protected final UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
diff --git a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
index e9258e9..202dcb1 100644
--- a/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
+++ b/tests/src/com/android/launcher3/ui/ActivityLeakTracker.java
@@ -26,9 +26,11 @@
 
 import java.util.WeakHashMap;
 
-class ActivityLeakTracker implements Application.ActivityLifecycleCallbacks {
+public class ActivityLeakTracker implements Application.ActivityLifecycleCallbacks {
     private final WeakHashMap<Activity, Boolean> mActivities = new WeakHashMap<>();
 
+    private int mActivitiesCreated;
+
     ActivityLeakTracker() {
         if (!TestHelpers.isInLauncherProcess()) return;
         final Application app =
@@ -36,9 +38,14 @@
         app.registerActivityLifecycleCallbacks(this);
     }
 
+    public int getActivitiesCreated() {
+        return mActivitiesCreated;
+    }
+
     @Override
     public void onActivityCreated(Activity activity, Bundle bundle) {
         mActivities.put(activity, true);
+        ++mActivitiesCreated;
     }
 
     @Override
@@ -77,7 +84,7 @@
             }
         }
 
-        if (liveActivities > 2)  return false;
+        if (liveActivities > 2) return false;
 
         // It's OK to have 1 leaked activity if no active activities exist.
         return liveActivities == 0 ? destroyedActivities <= 1 : destroyedActivities == 0;
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index debc736..d46845f 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -535,13 +535,14 @@
                 mExpectedRotation, mDevice.getDisplayRotation());
 
         // b/148422894
+        String error = null;
         for (int i = 0; i != 600; ++i) {
-            if (getNavigationModeMismatchError() == null) break;
+            error = getNavigationModeMismatchError();
+            if (error == null) break;
             sleep(100);
         }
-
-        final String error = getNavigationModeMismatchError();
         assertTrue(error, error == null);
+
         log("verifyContainerType: " + containerType);
 
         final UiObject2 container = verifyVisibleObjects(containerType);