Focus running task in overview grid

- calculateTaskSize now returns the Rect for focused task
- Introduced calculateGridTaskSize for non-focused task, and translate non-focused tasks to fit the grid
- As Task Rect is now vertically centered, removed ClearAllButton's grid vertical translation
- When ClearAllButton is not shown (e.g. quickswitch), make sure ClearAllButton's scroll won't be used when page snapping. This happens when page scrolls are translated so they're negative.
- Added back ActionsView when task is focused below the TaskView, which become invisible as you scroll
- In Modal state, move the ActionsView down so it won't cover the TaskView

Bug: 175939487
Test: manual
Change-Id: Idfa94a51f856418adc0503cf04211dcb4b1814fe
diff --git a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index e6af848..866eac6 100644
--- a/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
+++ b/go/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -14,11 +14,12 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- NOTE! don't add dimensions for margins / gravity to root view in this file, they need to be
+     loaded at runtime. -->
 <com.android.quickstep.views.GoOverviewActionsView
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal|bottom">
+    android:layout_height="wrap_content">
 
     <LinearLayout
         android:id="@+id/action_buttons"
diff --git a/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
index 1da25e7..b652252 100644
--- a/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
+++ b/quickstep/overview_ui_overrides/res/layout/overview_actions_container.xml
@@ -14,10 +14,11 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- NOTE! don't add dimensions for margins / gravity to root view in this file, they need to be
+     loaded at runtime. -->
 <com.android.quickstep.views.OverviewActionsView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal|bottom">
+    android:layout_height="wrap_content">
 
     <LinearLayout
         android:id="@+id/action_buttons"
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 3036341..3e6c78f 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -36,6 +36,7 @@
     <dimen name="overview_grid_bottom_margin">90dp</dimen>
     <dimen name="overview_grid_side_margin">54dp</dimen>
     <dimen name="overview_grid_row_spacing">42dp</dimen>
+    <dimen name="overview_grid_focus_vertical_margin">130dp</dimen>
     <dimen name="split_placeholder_size">110dp</dimen>
 
     <dimen name="recents_page_spacing">16dp</dimen>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index e1456b1..6929b5d 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -103,12 +103,11 @@
 
     private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
             LauncherState state) {
-        float clearAllButtonAlpha = (state.getVisibleElements(mLauncher) & CLEAR_ALL_BUTTON) != 0
-                ? 1 : 0;
+        float clearAllButtonAlpha = state.areElementsVisible(mLauncher, CLEAR_ALL_BUTTON) ? 1 : 0;
         propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                 clearAllButtonAlpha, LINEAR);
-        float overviewButtonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_ACTIONS) != 0
-                ? 1 : 0;
+        float overviewButtonAlpha = state.areElementsVisible(mLauncher, OVERVIEW_ACTIONS)
+                && mRecentsView.shouldShowOverviewActionsForState(state) ? 1 : 0;
         propertySetter.setFloat(mLauncher.getActionsView().getVisibilityAlpha(),
                 MultiValueAlpha.VALUE, overviewButtonAlpha, config.getInterpolator(
                         ANIM_OVERVIEW_ACTIONS_FADE, LINEAR));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index bdba482..6f084a1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -18,6 +18,7 @@
 import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 
 import com.android.launcher3.BaseDraggingActivity;
@@ -70,13 +71,12 @@
     }
 
     public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
-        Rect out = new Rect();
-        activity.<RecentsView>getOverviewPanel().getTaskSize(out);
-        int taskHeight = out.height();
-        activity.<RecentsView>getOverviewPanel().getModalTaskSize(out);
-        int newHeight = out.height();
+        Point taskSize = activity.<RecentsView>getOverviewPanel().getSelectedTaskSize();
+        Rect modalTaskSize = new Rect();
+        activity.<RecentsView>getOverviewPanel().getModalTaskSize(modalTaskSize);
 
-        float scale = (float) newHeight / taskHeight;
+        float scale = Math.min((float) modalTaskSize.height() / taskSize.y,
+                (float) modalTaskSize.width() / taskSize.x);
 
         return new float[] {scale, NO_OFFSET};
     }
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
index 43e70a3..32bf315 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewState.java
@@ -99,8 +99,7 @@
 
     @Override
     public int getVisibleElements(Launcher launcher) {
-        return displayOverviewTasksAsGrid(launcher.getDeviceProfile()) ? CLEAR_ALL_BUTTON
-                : CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
+        return CLEAR_ALL_BUTTON | OVERVIEW_ACTIONS;
     }
 
     @Override
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index 0415009..3afffc1 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -206,18 +206,17 @@
             Rect gridRect = new Rect();
             calculateGridSize(context, dp, gridRect);
 
-            int rowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
-            float rowHeight = (gridRect.height() - rowSpacing) / 2f;
+            int verticalMargin = res.getDimensionPixelSize(
+                    R.dimen.overview_grid_focus_vertical_margin);
+            float taskHeight = gridRect.height() - verticalMargin * 2;
 
             PointF taskDimension = getTaskDimension(context, dp);
-            float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / Math.max(
-                    taskDimension.x, taskDimension.y);
+            float scale = taskHeight / Math.max(taskDimension.x, taskDimension.y);
             int outWidth = Math.round(scale * taskDimension.x);
             int outHeight = Math.round(scale * taskDimension.y);
 
-            int gravity = Gravity.TOP;
+            int gravity = Gravity.CENTER_VERTICAL;
             gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
-            gridRect.inset(0, dp.overviewTaskThumbnailTopMarginPx, 0, 0);
             Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect);
         } else {
             int taskMargin = dp.overviewTaskMarginPx;
@@ -294,6 +293,30 @@
     }
 
     /**
+     * Calculates the overview grid non-focused task size for the provided device configuration.
+     */
+    public final void calculateGridTaskSize(Context context, DeviceProfile dp, Rect outRect,
+            PagedOrientationHandler orientedState) {
+        Resources res = context.getResources();
+        Rect gridRect = new Rect();
+        calculateGridSize(context, dp, gridRect);
+
+        int rowSpacing = res.getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
+        float rowHeight = (gridRect.height() - rowSpacing) / 2f;
+
+        PointF taskDimension = getTaskDimension(context, dp);
+        float scale = (rowHeight - dp.overviewTaskThumbnailTopMarginPx) / Math.max(
+                taskDimension.x, taskDimension.y);
+        int outWidth = Math.round(scale * taskDimension.x);
+        int outHeight = Math.round(scale * taskDimension.y);
+
+        int gravity = Gravity.TOP;
+        gravity |= orientedState.getRecentsRtlSetting(res) ? Gravity.RIGHT : Gravity.LEFT;
+        gridRect.inset(0, dp.overviewTaskThumbnailTopMarginPx, 0, 0);
+        Gravity.apply(gravity, outWidth, outHeight, gridRect, outRect);
+    }
+
+    /**
      * Calculates the modal taskView size for the provided device configuration
      */
     public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
index 03a0b4e..8962ec9 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsStateController.java
@@ -76,7 +76,9 @@
         float clearAllButtonAlpha = state.hasClearAllButton() ? 1 : 0;
         setter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
                 clearAllButtonAlpha, LINEAR);
-        float overviewButtonAlpha =  state.hasOverviewActions(mActivity) ? 1 : 0;
+        float overviewButtonAlpha =
+                state.hasOverviewActions() && mRecentsView.shouldShowOverviewActionsForState(state)
+                        ? 1 : 0;
         setter.setFloat(mActivity.getActionsView().getVisibilityAlpha(),
                 MultiValueAlpha.VALUE, overviewButtonAlpha, LINEAR);
 
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index e075045..854a140 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -42,7 +42,7 @@
 import java.util.ArrayList;
 
 @TargetApi(Build.VERSION_CODES.R)
-public class FallbackRecentsView extends RecentsView<RecentsActivity>
+public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsState>
         implements StateListener<RecentsState> {
 
     private RunningTaskInfo mHomeTaskInfo;
diff --git a/quickstep/src/com/android/quickstep/fallback/RecentsState.java b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
index a9856d2..bbe279e 100644
--- a/quickstep/src/com/android/quickstep/fallback/RecentsState.java
+++ b/quickstep/src/com/android/quickstep/fallback/RecentsState.java
@@ -99,8 +99,8 @@
     /**
      * For this state, whether overview actions should be shown.
      */
-    public boolean hasOverviewActions(RecentsActivity activity) {
-        return hasFlag(FLAG_OVERVIEW_ACTIONS) && !showAsGrid(activity.getDeviceProfile());
+    public boolean hasOverviewActions() {
+        return hasFlag(FLAG_OVERVIEW_ACTIONS);
     }
 
     public float[] getOverviewScaleAndOffset(RecentsActivity activity) {
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index ff78995..d616f7c 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -45,12 +45,13 @@
     private float mScrollAlpha = 1;
     private float mContentAlpha = 1;
     private float mVisibilityAlpha = 1;
+    private float mFullscreenProgress = 1;
     private float mGridProgress = 1;
 
     private boolean mIsRtl;
     private float mNormalTranslationPrimary;
+    private float mFullscreenTranslationPrimary;
     private float mGridTranslationPrimary;
-    private float mGridTranslationSecondary;
     private float mGridScrollOffset;
     private float mScrollOffsetPrimary;
 
@@ -130,13 +131,13 @@
         setClickable(Math.min(alpha, 1) == 1);
     }
 
-    public void setGridTranslationPrimary(float gridTranslationPrimary) {
-        mGridTranslationPrimary = gridTranslationPrimary;
+    public void setFullscreenTranslationPrimary(float fullscreenTranslationPrimary) {
+        mFullscreenTranslationPrimary = fullscreenTranslationPrimary;
         applyPrimaryTranslation();
     }
 
-    public void setGridTranslationSecondary(float gridTranslationSecondary) {
-        mGridTranslationSecondary = gridTranslationSecondary;
+    public void setGridTranslationPrimary(float gridTranslationPrimary) {
+        mGridTranslationPrimary = gridTranslationPrimary;
         applyPrimaryTranslation();
     }
 
@@ -148,8 +149,11 @@
         mScrollOffsetPrimary = scrollOffsetPrimary;
     }
 
-    public float getScrollAdjustment(boolean gridEnabled) {
+    public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
         float scrollAdjustment = 0;
+        if (fullscreenEnabled) {
+            scrollAdjustment += mFullscreenTranslationPrimary;
+        }
         if (gridEnabled) {
             scrollAdjustment += mGridTranslationPrimary + mGridScrollOffset;
         }
@@ -157,8 +161,18 @@
         return scrollAdjustment;
     }
 
-    public float getOffsetAdjustment(boolean gridEnabled) {
-        return getScrollAdjustment(gridEnabled);
+    public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
+        return getScrollAdjustment(fullscreenEnabled, gridEnabled);
+    }
+
+    /**
+     * Adjust translation when this TaskView is about to be shown fullscreen.
+     *
+     * @param progress: 0 = no translation; 1 = translate according to TaskVIew translations.
+     */
+    public void setFullscreenProgress(float progress) {
+        mFullscreenProgress = progress;
+        applyPrimaryTranslation();
     }
 
     /**
@@ -180,7 +194,8 @@
         PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
         orientationHandler.getPrimaryViewTranslate().set(this,
                 orientationHandler.getPrimaryValue(0f, getOriginalTranslationY())
-                        + mNormalTranslationPrimary + getGridTrans(mGridTranslationPrimary));
+                        + mNormalTranslationPrimary + getFullscreenTrans(
+                        mFullscreenTranslationPrimary) + getGridTrans(mGridTranslationPrimary));
     }
 
     private void applySecondaryTranslation() {
@@ -191,8 +206,11 @@
 
         PagedOrientationHandler orientationHandler = recentsView.getPagedOrientationHandler();
         orientationHandler.getSecondaryViewTranslate().set(this,
-                orientationHandler.getSecondaryValue(0f, getOriginalTranslationY())
-                        + getGridTrans(mGridTranslationSecondary));
+                orientationHandler.getSecondaryValue(0f, getOriginalTranslationY()));
+    }
+
+    private float getFullscreenTrans(float endTranslation) {
+        return mFullscreenProgress > 0 ? endTranslation : 0;
     }
 
     private float getGridTrans(float endTranslation) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index a0af68a..896d1ae 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -19,11 +19,13 @@
 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.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.LauncherState.SPRING_LOADED;
 import static com.android.quickstep.util.NavigationModeFeatureFlag.LIVE_TILE;
 
 import android.annotation.TargetApi;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.os.Build;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -35,6 +37,7 @@
 import com.android.launcher3.statehandlers.DepthController;
 import com.android.launcher3.statemanager.StateManager.StateListener;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
+import com.android.launcher3.util.SplitConfigurationOptions;
 import com.android.quickstep.LauncherActivityInterface;
 import com.android.quickstep.util.OverviewToHomeAnim;
 import com.android.systemui.plugins.PluginListener;
@@ -44,7 +47,7 @@
  * {@link RecentsView} used in Launcher activity
  */
 @TargetApi(Build.VERSION_CODES.O)
-public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
+public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher, LauncherState>
         implements StateListener<LauncherState> {
 
     private RecentsExtraCard mRecentsExtraCardPlugin;
@@ -234,4 +237,33 @@
             }
         }
     }
+
+    @Override
+    protected void onDismissAnimationEnds() {
+        super.onDismissAnimationEnds();
+        if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
+            // We want to keep the tasks translations in this temporary state
+            // after resetting the rest above
+            setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation);
+            setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation);
+        }
+    }
+
+    @Override
+    public void initiateSplitSelect(TaskView taskView,
+            SplitConfigurationOptions.SplitPositionOption splitPositionOption) {
+        super.initiateSplitSelect(taskView, splitPositionOption);
+        mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        // If overview is in modal state when rotate, reset it to overview state without running
+        // animation.
+        if (mActivity.isInState(OVERVIEW_MODAL_TASK)) {
+            mActivity.getStateManager().goToState(LauncherState.OVERVIEW, false);
+            resetModalVisuals();
+        }
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 6fcd54c..9adeea4 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -16,6 +16,7 @@
 
 package com.android.quickstep.views;
 
+import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
 import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SHARE;
 
 import android.content.Context;
@@ -32,6 +33,7 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 import com.android.launcher3.util.MultiValueAlpha;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.SysUINavigationMode;
@@ -76,6 +78,7 @@
     private static final int INDEX_VISIBILITY_ALPHA = 1;
     private static final int INDEX_FULLSCREEN_ALPHA = 2;
     private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3;
+    private static final int INDEX_SCROLL_ALPHA = 4;
 
     private final MultiValueAlpha mMultiValueAlpha;
 
@@ -87,6 +90,9 @@
 
     protected T mCallbacks;
 
+    private float mModalness;
+    private float mModalTransformY;
+
     public OverviewActionsView(Context context) {
         this(context, null);
     }
@@ -97,7 +103,7 @@
 
     public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr, 0);
-        mMultiValueAlpha = new MultiValueAlpha(this, 4);
+        mMultiValueAlpha = new MultiValueAlpha(this, 5);
         mMultiValueAlpha.setUpdateVisibility(true);
     }
 
@@ -189,6 +195,10 @@
         return mMultiValueAlpha.getProperty(INDEX_FULLSCREEN_ALPHA);
     }
 
+    public AlphaProperty getScrollAlpha() {
+        return mMultiValueAlpha.getProperty(INDEX_SCROLL_ALPHA);
+    }
+
     private void updateHorizontalPadding() {
         setPadding(mInsets.left, 0, mInsets.right, 0);
     }
@@ -224,4 +234,27 @@
         bottomMargin += mInsets.bottom;
         return bottomMargin;
     }
+
+    /**
+     * 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.
+     */
+    public void setTaskModalness(float modalness) {
+        mModalness = modalness;
+        applyTranslationY();
+    }
+
+    public void setModalTransformY(float modalTransformY) {
+        mModalTransformY = modalTransformY;
+        applyTranslationY();
+    }
+
+    private void applyTranslationY() {
+        setTranslationY(getModalTrans(mModalTransformY));
+    }
+
+    private float getModalTrans(float endTranslation) {
+        float progress = ACCEL_DEACCEL.getInterpolation(mModalness);
+        return Utilities.mapRange(progress, 0, endTranslation);
+    }
 }
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index ad266ce..b688f1e 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -25,8 +25,6 @@
 import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS;
 import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
 import static com.android.launcher3.LauncherState.BACKGROUND_APP;
-import static com.android.launcher3.LauncherState.OVERVIEW_MODAL_TASK;
-import static com.android.launcher3.LauncherState.OVERVIEW_SPLIT_SELECT;
 import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
 import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
 import static com.android.launcher3.Utilities.mapToRange;
@@ -81,6 +79,7 @@
 import android.util.FloatProperty;
 import android.util.Log;
 import android.util.SparseBooleanArray;
+import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -91,6 +90,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Interpolator;
+import android.widget.FrameLayout;
 import android.widget.ListView;
 
 import androidx.annotation.Nullable;
@@ -100,7 +100,6 @@
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Insettable;
 import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.PagedView;
 import com.android.launcher3.R;
 import com.android.launcher3.Utilities;
@@ -111,6 +110,7 @@
 import com.android.launcher3.compat.AccessibilityManagerCompat;
 import com.android.launcher3.config.FeatureFlags;
 import com.android.launcher3.statehandlers.DepthController;
+import com.android.launcher3.statemanager.BaseState;
 import com.android.launcher3.statemanager.StatefulActivity;
 import com.android.launcher3.testing.TestProtocol;
 import com.android.launcher3.touch.PagedOrientationHandler;
@@ -161,8 +161,9 @@
  * A list of recent tasks.
  */
 @TargetApi(Build.VERSION_CODES.R)
-public abstract class RecentsView<T extends StatefulActivity> extends PagedView implements
-        Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
+public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>,
+        STATE_TYPE extends BaseState<STATE_TYPE>> extends PagedView implements Insettable,
+        TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback,
         InvariantDeviceProfile.OnIDPChangeListener, TaskVisualsChangeListener,
         SplitScreenBounds.OnChangeListener {
 
@@ -295,7 +296,7 @@
             };
 
     protected final RecentsOrientedState mOrientationState;
-    protected final BaseActivityInterface mSizeStrategy;
+    protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
     protected RecentsAnimationController mRecentsAnimationController;
     protected SurfaceTransactionApplier mSyncTransactionApplier;
     protected int mTaskWidth;
@@ -304,6 +305,7 @@
     protected final TaskViewSimulator mLiveTileTaskViewSimulator;
     protected final Rect mLastComputedTaskSize = new Rect();
     protected final Rect mLastComputedGridSize = new Rect();
+    protected final Rect mLastComputedGridTaskSize = new Rect();
     // How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
     protected Float mLastComputedTaskPushOutDistance = null;
     protected boolean mEnableDrawingLiveTile = false;
@@ -317,10 +319,11 @@
     // The threshold at which we update the SystemUI flags when animating from the task into the app
     public static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.85f;
 
-    protected final T mActivity;
+    protected final ACTIVITY_TYPE mActivity;
     private final float mFastFlingVelocity;
     private final RecentsModel mModel;
     private final int mRowSpacing;
+    private final int mGridSideMargin;
     private final ClearAllButton mClearAllButton;
     private final Rect mClearAllButtonDeadZoneRect = new Rect();
     private final Rect mTaskViewDeadZoneRect = new Rect();
@@ -347,8 +350,8 @@
     private boolean mOverviewFullscreenEnabled;
 
     private float mAdjacentPageOffset = 0;
-    private float mTaskViewsSecondaryTranslation = 0;
-    private float mTaskViewsPrimaryTranslation = 0;
+    protected float mTaskViewsSecondaryTranslation = 0;
+    protected float mTaskViewsPrimaryTranslation = 0;
     // Progress from 0 to 1 where 0 is a carousel and 1 is a 2 row grid.
     private float mGridProgress = 0;
 
@@ -436,6 +439,8 @@
     protected int mRunningTaskId = -1;
     protected boolean mRunningTaskTileHidden;
     private Task mTmpRunningTask;
+    protected int mFocusedTaskId = -1;
+    private float mFocusedTaskRatio;
 
     private boolean mRunningTaskIconScaledDown = false;
 
@@ -541,6 +546,7 @@
         mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
         setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
         mRowSpacing = getResources().getDimensionPixelSize(R.dimen.overview_grid_row_spacing);
+        mGridSideMargin = getResources().getDimensionPixelSize(R.dimen.overview_grid_side_margin);
         mSquaredTouchSlop = squaredTouchSlop(context);
 
         mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents);
@@ -645,7 +651,6 @@
         mActionsView = actionsView;
         mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, getTaskViewCount() == 0);
         mSplitPlaceholderView = splitPlaceholderView;
-
     }
 
     public SplitPlaceholderView getSplitPlaceholder() {
@@ -1119,6 +1124,7 @@
         for (int i = 0; i < taskCount; i++) {
             getTaskViewAt(i).setFullscreenProgress(mFullscreenProgress);
         }
+        mClearAllButton.setFullscreenProgress(fullscreenProgress);
 
         // Fade out the actions view quickly (0.1 range)
         mActionsView.getFullscreenAlpha().setValue(
@@ -1158,9 +1164,31 @@
 
         mSizeStrategy.calculateGridSize(mActivity, mActivity.getDeviceProfile(),
                 mLastComputedGridSize);
+        mSizeStrategy.calculateGridTaskSize(mActivity, mActivity.getDeviceProfile(),
+                mLastComputedGridTaskSize, mOrientationHandler);
 
         // Force TaskView to update size from thumbnail
         updateTaskSize();
+
+        // Update ActionsView position
+        FrameLayout.LayoutParams layoutParams =
+                (FrameLayout.LayoutParams) mActionsView.getLayoutParams();
+        if (dp.isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
+            layoutParams.gravity = Gravity.BOTTOM;
+            layoutParams.bottomMargin =
+                    dp.heightPx - mInsets.bottom - mLastComputedGridSize.bottom;
+            layoutParams.leftMargin = mLastComputedTaskSize.left;
+            layoutParams.rightMargin = dp.widthPx - mLastComputedTaskSize.right;
+            // When in modal state, remove bottom margin to avoid covering content.
+            mActionsView.setModalTransformY(layoutParams.bottomMargin);
+        } else {
+            layoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
+            layoutParams.bottomMargin = 0;
+            layoutParams.leftMargin = 0;
+            layoutParams.rightMargin = 0;
+            mActionsView.setModalTransformY(0);
+        }
+        mActionsView.setLayoutParams(layoutParams);
     }
 
     /**
@@ -1201,6 +1229,8 @@
             getTaskViewAt(i).setFullscreenTranslationX(
                     fullscreenTranslations[i] - fullscreenTranslations[firstNonHomeTaskIndex]);
         }
+        mClearAllButton.setFullscreenTranslationPrimary(
+                accumulatedTranslationX - fullscreenTranslations[firstNonHomeTaskIndex]);
 
         // Align ClearAllButton to the left (RTL) or right (non-RTL), which is different from other
         // TaskViews.
@@ -1216,11 +1246,36 @@
         mLastComputedTaskSize.set(outRect);
     }
 
+    /**
+     * Returns the size of task selected to enter modal state.
+     */
+    public Point getSelectedTaskSize() {
+        mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(), mTempRect,
+                mOrientationHandler);
+        int taskWidth = mTempRect.width();
+        int taskHeight = mTempRect.height();
+        if (mRunningTaskId != -1) {
+            int boxLength = Math.max(taskWidth, taskHeight);
+            if (mFocusedTaskRatio > 1) {
+                taskWidth = boxLength;
+                taskHeight = (int) (boxLength / mFocusedTaskRatio);
+            } else {
+                taskWidth = (int) (boxLength * mFocusedTaskRatio);
+                taskHeight = boxLength;
+            }
+        }
+        return new Point(taskWidth, taskHeight);
+    }
+
     /** Gets the last computed task size */
     public Rect getLastComputedTaskSize() {
         return mLastComputedTaskSize;
     }
 
+    public Rect getLastComputedGridTaskSize() {
+        return mLastComputedGridTaskSize;
+    }
+
     /** Gets the task size for modal state. */
     public void getModalTaskSize(Rect outRect) {
         mSizeStrategy.calculateModalTaskSize(mActivity, mActivity.getDeviceProfile(), outRect);
@@ -1239,6 +1294,15 @@
 
             // After scrolling, update the visible task's data
             loadVisibleTaskData(TaskView.FLAG_UPDATE_ALL);
+
+            // After scrolling, update ActionsView's visibility.
+            TaskView focusedTaskView = getFocusedTaskView();
+            if (focusedTaskView != null) {
+                float scrollDiff = Math.abs(getScrollForPage(indexOfChild(focusedTaskView))
+                        - mOrientationHandler.getPrimaryScroll(this));
+                float delta = (mGridSideMargin - scrollDiff) / (float) mGridSideMargin;
+                mActionsView.getScrollAlpha().setValue(Utilities.boundToRange(delta, 0, 1));
+            }
         }
 
         // Update the high res thumbnail loader state
@@ -1403,6 +1467,7 @@
         setCurrentTask(-1);
         mIgnoreResetTaskId = -1;
         mTaskListChangeId = -1;
+        mFocusedTaskId = -1;
 
         mRecentsAnimationController = null;
         mLiveTileParams.setTargetSet(null);
@@ -1429,6 +1494,17 @@
         return getTaskIndexForId(mRunningTaskId);
     }
 
+    public @Nullable TaskView getFocusedTaskView() {
+        return getTaskView(mFocusedTaskId);
+    }
+
+    /**
+     * Returns the width to height ratio of the focused {@link TaskView}.
+     */
+    public float getFocusedTaskRatio() {
+        return mFocusedTaskRatio;
+    }
+
     /**
      * Get the index of the task view whose id matches {@param taskId}.
      * @return -1 if there is no task view for the task id, else the index of the task view.
@@ -1520,6 +1596,11 @@
      */
     public void onGestureEndTargetCalculated(GestureState.GestureEndTarget endTarget) {
         mCurrentGestureEndTarget = endTarget;
+        if (showAsGrid()) {
+            mFocusedTaskId = mRunningTaskId;
+            mFocusedTaskRatio =
+                    mLastComputedTaskSize.width() / (float) mLastComputedTaskSize.height();
+        }
     }
 
     /**
@@ -1540,8 +1621,7 @@
         setRunningTaskHidden(false);
         animateUpRunningTaskIconScale();
 
-        // TODO: This should be tied to whether there is a focus app on overview.
-        if (!showAsGrid()) {
+        if (!showAsGrid() || getFocusedTaskView() != null) {
             animateActionsViewIn();
         }
 
@@ -1661,6 +1741,13 @@
         anim.start();
     }
 
+    private void animateActionsViewOut() {
+        ObjectAnimator anim = ObjectAnimator.ofFloat(
+                mActionsView.getVisibilityAlpha(), MultiValueAlpha.VALUE, 1, 0);
+        anim.setDuration(TaskView.SCALE_ICON_DURATION);
+        anim.start();
+    }
+
     public void animateUpRunningTaskIconScale() {
         animateUpRunningTaskIconScale(0);
     }
@@ -1686,8 +1773,12 @@
             return;
         }
 
-        final int boxLength = Math.max(mTaskWidth, mTaskHeight);
+        final int boxLength = Math.max(mLastComputedGridTaskSize.width(),
+                mLastComputedGridTaskSize.height());
         int taskTopMargin = mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+        float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
+        float taskGridVerticalDiff = mLastComputedGridTaskSize.top - mLastComputedTaskSize.top;
+
         int topRowWidth = 0;
         int bottomRowWidth = 0;
         float topAccumulatedTranslationX = 0;
@@ -1710,15 +1801,23 @@
             }
 
             // Evenly distribute tasks between rows unless rearranging due to task dismissal, in
-            // which case keep tasks in their respective rows.
-            if ((!isTaskDismissal && topRowWidth <= bottomRowWidth) || (isTaskDismissal && mTopIdSet
-                    .contains(taskView.getTask().key.id))) {
+            // which case keep tasks in their respective rows. For the running task, don't join
+            // the grid.
+            if (taskView.isRunningTask()) {
+                topRowWidth += taskView.getLayoutParams().width + mPageSpacing;
+                bottomRowWidth += taskView.getLayoutParams().width + mPageSpacing;
+
+                // Center view vertically in case it's from different orientation.
+                taskView.setGridTranslationY((mLastComputedTaskSize.height() + taskTopMargin
+                        - taskView.getLayoutParams().height) / 2f);
+            } else if ((!isTaskDismissal && topRowWidth <= bottomRowWidth) || (isTaskDismissal
+                        && mTopIdSet.contains(taskView.getTask().key.id))) {
                 gridTranslations[i] += topAccumulatedTranslationX;
                 topRowWidth += taskView.getLayoutParams().width + mPageSpacing;
                 topSet.add(i);
                 mTopIdSet.add(taskView.getTask().key.id);
 
-                taskView.setGridTranslationY(0);
+                taskView.setGridTranslationY(taskGridVerticalDiff);
 
                 // Move horizontally into empty space.
                 float widthOffset = 0;
@@ -1735,8 +1834,7 @@
                 bottomSet.add(i);
 
                 // Move into bottom row.
-                float heightOffset = (boxLength + taskTopMargin) + mRowSpacing;
-                taskView.setGridTranslationY(heightOffset);
+                taskView.setGridTranslationY(heightOffset + taskGridVerticalDiff);
 
                 // Move horizontally into empty space.
                 float widthOffset = 0;
@@ -1794,8 +1892,6 @@
 
         mClearAllButton.setGridTranslationPrimary(
                 clearAllTotalTranslationX - gridTranslations[firstNonHomeTaskIndex]);
-        mClearAllButton.setGridTranslationSecondary(
-                boxLength - mTaskHeight / 2f + mRowSpacing / 2f);
         mClearAllButton.setGridScrollOffset(
                 mIsRtl ? mLastComputedTaskSize.left - mLastComputedGridSize.left
                         : mLastComputedTaskSize.right - mLastComputedGridSize.right);
@@ -2072,24 +2168,25 @@
                         snapToPageImmediately(pageToSnapTo);
                         // Grid got messed up, reapply.
                         updateGridProperties(true);
+                        if (showAsGrid() && getFocusedTaskView() == null) {
+                            animateActionsViewOut();
+                        }
                     }
                     // Update the layout synchronously so that the position of next view is
                     // immediately available.
                     onLayout(false /*  changed */, getLeft(), getTop(), getRight(), getBottom());
                 }
                 resetTaskVisuals();
-                if (mActivity.isInState(OVERVIEW_SPLIT_SELECT)) {
-                    // We want to keep the tasks translations in this temporary state
-                    // after resetting the rest above
-                    setTaskViewsResistanceTranslation(mTaskViewsSecondaryTranslation);
-                    setTaskViewsPrimaryTranslation(mTaskViewsPrimaryTranslation);
-                }
+                onDismissAnimationEnds();
                 mPendingAnimation = null;
             }
         });
         return anim;
     }
 
+    protected void onDismissAnimationEnds() {
+    }
+
     public PendingAnimation createAllTasksDismissAnimation(long duration) {
         if (FeatureFlags.IS_STUDIO_BUILD && mPendingAnimation != null) {
             throw new IllegalStateException("Another pending animation is still running");
@@ -2257,13 +2354,6 @@
         if (mOrientationState.setRecentsRotation(rotation)) {
             updateOrientationHandler();
         }
-
-        // If overview is in modal state when rotate, reset it to overview state without running
-        // animation.
-        if (mActivity.isInState(OVERVIEW_MODAL_TASK)) {
-            mActivity.getStateManager().goToState(LauncherState.OVERVIEW, false);
-            resetModalVisuals();
-        }
     }
 
     public void setLayoutRotation(int touchRotation, int displayRotation) {
@@ -2475,7 +2565,7 @@
         return distanceToOffscreen * offsetProgress;
     }
 
-    private void setTaskViewsResistanceTranslation(float translation) {
+    protected void setTaskViewsResistanceTranslation(float translation) {
         mTaskViewsSecondaryTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView task = getTaskViewAt(i);
@@ -2484,7 +2574,7 @@
         mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
     }
 
-    private void setTaskViewsPrimaryTranslation(float translation) {
+    protected void setTaskViewsPrimaryTranslation(float translation) {
         mTaskViewsPrimaryTranslation = translation;
         for (int i = 0; i < getTaskViewCount(); i++) {
             TaskView task = getTaskViewAt(i);
@@ -2515,7 +2605,6 @@
         mSplitPlaceholderView.getSplitController().setInitialTaskSelect(taskView,
                 splitPositionOption);
         mSplitHiddenTaskViewIndex = indexOfChild(taskView);
-        mActivity.getStateManager().goToState(LauncherState.OVERVIEW_SPLIT_SELECT);
     }
 
     public PendingAnimation createSplitSelectInitAnimation() {
@@ -2943,10 +3032,7 @@
      * Returns page scroll of the left most child.
      */
     public int getLeftMostChildScroll() {
-        if (mIsRtl) {
-            return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
-        }
-        return getScrollForPage(mTaskViewStartIndex);
+        return getScrollForPage(mIsRtl ? indexOfChild(mClearAllButton) : mTaskViewStartIndex);
     }
 
     @Override
@@ -2957,10 +3043,7 @@
                 // so use the rightmost task as the min scroll.
                 return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)));
             }
-            if (mIsRtl) {
-                return getScrollForPage(mTaskViewStartIndex);
-            }
-            return getScrollForPage(indexOfChild(getTaskViewAt(getTaskViewCount() - 1)) + 1);
+            return getScrollForPage(mIsRtl ? mTaskViewStartIndex : indexOfChild(mClearAllButton));
         }
         return super.computeMaxScroll();
     }
@@ -2979,7 +3062,8 @@
                 scrollDiff = ((TaskView) child).getScrollAdjustment(mOverviewFullscreenEnabled,
                         showAsGrid());
             } else if (child instanceof ClearAllButton) {
-                scrollDiff = ((ClearAllButton) child).getScrollAdjustment(showAsGrid());
+                scrollDiff = ((ClearAllButton) child).getScrollAdjustment(
+                        mOverviewFullscreenEnabled, showAsGrid());
             }
 
             if (scrollDiff != 0) {
@@ -2998,7 +3082,8 @@
             childOffset += ((TaskView) child).getOffsetAdjustment(mOverviewFullscreenEnabled,
                     showAsGrid());
         } else if (child instanceof ClearAllButton) {
-            childOffset += ((ClearAllButton) child).getOffsetAdjustment(showAsGrid());
+            childOffset += ((ClearAllButton) child).getOffsetAdjustment(mOverviewFullscreenEnabled,
+                    showAsGrid());
         }
         return childOffset;
     }
@@ -3174,6 +3259,7 @@
         boolean inPlaceLandscape = !mOrientationState.canRecentsActivityRotate()
                 && mOrientationState.getTouchRotation() != ROTATION_0;
         mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
+        mActionsView.setTaskModalness(modalness);
     }
 
     @Nullable
@@ -3208,6 +3294,11 @@
                 mCurrentGestureEndTarget).displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));
     }
 
+    public boolean shouldShowOverviewActionsForState(STATE_TYPE state) {
+        return !state.displayOverviewTasksAsGrid(mActivity.getDeviceProfile())
+                || getFocusedTaskView() != null;
+    }
+
     /**
      * Used to register callbacks for when our empty message state changes.
      *
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 21b1164..745e328 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -987,7 +987,7 @@
         return scrollAdjustment;
     }
 
-    public float getOffsetAdjustment(boolean fullscreenEnabled,boolean gridEnabled) {
+    public float getOffsetAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
         return getScrollAdjustment(fullscreenEnabled, gridEnabled);
     }
 
@@ -1213,62 +1213,77 @@
      */
     void updateTaskSize() {
         ViewGroup.LayoutParams params = getLayoutParams();
+        float fullscreenScale;
+        float boxTranslationY;
+        int expectedWidth;
+        int expectedHeight;
         if (mActivity.getDeviceProfile().isTablet && FeatureFlags.ENABLE_OVERVIEW_GRID.get()) {
             final int thumbnailPadding =
                     mActivity.getDeviceProfile().overviewTaskThumbnailTopMarginPx;
+            final Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
+            final int taskWidth = lastComputedTaskSize.width();
+            final int taskHeight = lastComputedTaskSize.height();
 
-            Rect lastComputedTaskSize = getRecentsView().getLastComputedTaskSize();
-            int taskWidth = lastComputedTaskSize.width();
-            int taskHeight = lastComputedTaskSize.height();
-
-            int expectedWidth;
-            int expectedHeight;
-            float thumbnailRatio = mTask != null ? mTask.getVisibleThumbnailRatio(
-                    TaskView.CLIP_STATUS_AND_NAV_BARS) : 0f;
-            if (isRunningTask() || thumbnailRatio == 0f) {
-                expectedWidth = taskWidth;
-                expectedHeight = taskHeight + thumbnailPadding;
+            int boxWidth;
+            int boxHeight;
+            float thumbnailRatio;
+            boolean isFocusedTask = isFocusedTask();
+            if (isFocusedTask || isRunningTask()) {
+                // Task will be focused and should use focused task size. Use runningTaskRatio
+                // that is associated with the original orientation of the focused task if possible.
+                boxWidth = taskWidth;
+                boxHeight = taskHeight;
+                thumbnailRatio = isFocusedTask ? getRecentsView().getFocusedTaskRatio() : 0;
             } else {
-                int boxLength = Math.max(taskWidth, taskHeight);
-                if (thumbnailRatio > 1) {
-                    expectedWidth = boxLength;
-                    expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding;
-                } else {
-                    expectedWidth = (int) (boxLength * thumbnailRatio);
-                    expectedHeight = boxLength + thumbnailPadding;
-                }
+                // Otherwise task is in grid, and should use lastComputedGridTaskSize.
+                Rect lastComputedGridTaskSize = getRecentsView().getLastComputedGridTaskSize();
+                boxWidth = lastComputedGridTaskSize.width();
+                boxHeight = lastComputedGridTaskSize.height();
+                thumbnailRatio = mTask != null ? mTask.getVisibleThumbnailRatio(
+                        TaskView.CLIP_STATUS_AND_NAV_BARS) : 0f;
+            }
+            int boxLength = Math.max(boxWidth, boxHeight);
+
+            // Bound width/height to the box size.
+            if (thumbnailRatio == 0f) {
+                expectedWidth = boxWidth;
+                expectedHeight = boxHeight + thumbnailPadding;
+            } else if (thumbnailRatio > 1) {
+                expectedWidth = boxLength;
+                expectedHeight = (int) (boxLength / thumbnailRatio) + thumbnailPadding;
+            } else {
+                expectedWidth = (int) (boxLength * thumbnailRatio);
+                expectedHeight = boxLength + thumbnailPadding;
             }
 
-            float heightDiff = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
-            setBoxTranslationY(heightDiff);
+            // Scale to to fit task Rect.
+            fullscreenScale = taskWidth / (float) boxWidth;
 
-            float fullscreenScale = 1f;
-            if (expectedWidth > taskWidth) {
-                // In full screen, expectedWidth should not be larger than taskWidth.
-                fullscreenScale = taskWidth / (float) expectedWidth;
-            } else if (expectedHeight - thumbnailPadding > taskHeight) {
-                // In full screen, expectedHeight should not be larger than taskHeight.
-                fullscreenScale = taskHeight / (float) (expectedHeight - thumbnailPadding);
+            // In full screen, scale back TaskView to original size.
+            if (expectedWidth > boxWidth) {
+                fullscreenScale *= boxWidth / (float) expectedWidth;
+            } else if (expectedHeight - thumbnailPadding > boxHeight) {
+                fullscreenScale *= boxHeight / (float) (expectedHeight - thumbnailPadding);
             }
-            setFullscreenScale(fullscreenScale);
 
-            if (params.width != expectedWidth || params.height != expectedHeight) {
-                params.width = expectedWidth;
-                params.height = expectedHeight;
-                setLayoutParams(params);
-            }
+            // Align to top of task Rect.
+            boxTranslationY = (expectedHeight - thumbnailPadding - taskHeight) / 2.0f;
         } else {
-            setBoxTranslationY(0);
-            setFullscreenScale(1);
-            if (params.width != ViewGroup.LayoutParams.MATCH_PARENT) {
-                params.width = ViewGroup.LayoutParams.MATCH_PARENT;
-                params.height = ViewGroup.LayoutParams.MATCH_PARENT;
-                setLayoutParams(params);
-            }
+            fullscreenScale = 1f;
+            boxTranslationY = 0f;
+            expectedWidth = ViewGroup.LayoutParams.MATCH_PARENT;
+            expectedHeight = ViewGroup.LayoutParams.MATCH_PARENT;
+        }
+
+        setFullscreenScale(fullscreenScale);
+        setBoxTranslationY(boxTranslationY);
+        if (params.width != expectedWidth || params.height != expectedHeight) {
+            params.width = expectedWidth;
+            params.height = expectedHeight;
+            setLayoutParams(params);
         }
     }
 
-
     private float getFullscreenTrans(float endTranslation) {
         float progress = ACCEL_DEACCEL.getInterpolation(mFullscreenProgress);
         return Utilities.mapRange(progress, 0, endTranslation);
@@ -1281,6 +1296,13 @@
         return this == getRecentsView().getRunningTaskView();
     }
 
+    public boolean isFocusedTask() {
+        if (getRecentsView() == null) {
+            return false;
+        }
+        return this == getRecentsView().getFocusedTaskView();
+    }
+
     public void setShowScreenshot(boolean showScreenshot) {
         mShowScreenshot = showScreenshot;
     }
diff --git a/res/layout/overview_actions_container.xml b/res/layout/overview_actions_container.xml
index 5946bf6..f152c7c 100644
--- a/res/layout/overview_actions_container.xml
+++ b/res/layout/overview_actions_container.xml
@@ -14,6 +14,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+<!-- NOTE! don't add dimensions for margins / gravity to root view in this file, they need to be
+     loaded at runtime. -->
 <Space
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="0dp"