Animate grid tasks to modal state.
Bug: 265641913
Test: OverviewActionsControllerTest.java
Change-Id: I5cfa5bc3a9e0fdc024d0d709bfb2b5eccd6ea5a2
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
index 0c49e5f..b9221ee 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/OverviewModalTaskState.java
@@ -18,12 +18,12 @@
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;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.views.RecentsView;
/**
@@ -70,13 +70,22 @@
}
}
- public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
- Point taskSize = activity.<RecentsView>getOverviewPanel().getSelectedTaskSize();
- Rect modalTaskSize = new Rect();
- activity.<RecentsView>getOverviewPanel().getModalTaskSize(modalTaskSize);
+ @Override
+ public boolean isTaskbarStashed(Launcher launcher) {
+ if (FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+ return true;
+ }
+ return super.isTaskbarStashed(launcher);
+ }
- float scale = Math.min((float) modalTaskSize.height() / taskSize.y,
- (float) modalTaskSize.width() / taskSize.x);
+ public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
+ RecentsView recentsView = activity.<RecentsView>getOverviewPanel();
+ Rect taskSize = recentsView.getSelectedTaskBounds();
+ Rect modalTaskSize = new Rect();
+ recentsView.getModalTaskSize(modalTaskSize);
+
+ float scale = Math.min((float) modalTaskSize.height() / taskSize.height(),
+ (float) modalTaskSize.width() / taskSize.width());
return new float[] {scale, NO_OFFSET};
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index f9ad749..998439e 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -52,6 +52,7 @@
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.statemanager.BaseState;
@@ -339,12 +340,21 @@
*/
public final void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
calculateTaskSize(context, dp, outRect);
- float maxScale = context.getResources().getFloat(R.dimen.overview_modal_max_scale);
+ boolean isGridOnlyOverview = dp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get();
+ int claimedSpaceBelow = isGridOnlyOverview
+ ? dp.overviewActionsTopMarginPx + dp.overviewActionsHeight + dp.stashedTaskbarSize
+ : (dp.heightPx - outRect.bottom - dp.getInsets().bottom);
+ int minimumHorizontalPadding = 0;
+ if (!isGridOnlyOverview) {
+ float maxScale = context.getResources().getFloat(R.dimen.overview_modal_max_scale);
+ minimumHorizontalPadding =
+ Math.round((dp.availableWidthPx - outRect.width() * maxScale) / 2);
+ }
calculateTaskSizeInternal(
context, dp,
dp.overviewTaskMarginPx,
- dp.heightPx - outRect.bottom - dp.getInsets().bottom,
- Math.round((dp.availableWidthPx - outRect.width() * maxScale) / 2),
+ claimedSpaceBelow,
+ minimumHorizontalPadding,
1f /*maxScale*/,
Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM,
outRect);
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 0dcd723..72330ef 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -80,9 +80,9 @@
boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
boolean isTablet = activity.getDeviceProfile().isTablet;
+ boolean isGridOnlyOverview = isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get();
// Add overview actions to the menu when in in-place rotate landscape mode.
- if ((!canLauncherRotate && isInLandscape)
- || (isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get())) {
+ if ((!canLauncherRotate && isInLandscape) || isGridOnlyOverview) {
// Add screenshot action to task menu.
List<SystemShortcut> screenshotShortcuts = TaskShortcutFactory.SCREENSHOT
.getShortcuts(activity, taskContainer);
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index db6d56b..4b1dd43 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.fallback;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
import static com.android.quickstep.fallback.RecentsState.HOME;
@@ -200,8 +202,9 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState, boolean animate) {
- if (isModalState) {
+ public void setModalStateEnabled(int taskId, boolean animate) {
+ if (taskId != INVALID_TASK_ID) {
+ setSelectedTask(taskId);
mActivity.getStateManager().goToState(RecentsState.MODAL_TASK, animate);
} else {
if (mActivity.isInState(RecentsState.MODAL_TASK)) {
diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
index ff26129..c165acc 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep.views;
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+
import static com.android.launcher3.LauncherState.CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -171,8 +173,9 @@
}
@Override
- public void setModalStateEnabled(boolean isModalState, boolean animate) {
- if (isModalState) {
+ public void setModalStateEnabled(int taskId, boolean animate) {
+ if (taskId != INVALID_TASK_ID) {
+ setSelectedTask(taskId);
mActivity.getStateManager().goToState(LauncherState.OVERVIEW_MODAL_TASK, animate);
} else {
if (mActivity.isInState(LauncherState.OVERVIEW_MODAL_TASK)) {
diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
index 0d21e60..f578b87 100644
--- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
+++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java
@@ -303,6 +303,10 @@
return mDp.getOverviewActionsClaimedSpaceBelow();
}
+ if (mDp.isTablet && FeatureFlags.ENABLE_GRID_ONLY_OVERVIEW.get()) {
+ return mDp.stashedTaskbarSize;
+ }
+
// Align to bottom of task Rect.
return mDp.heightPx - mTaskSize.bottom - mDp.overviewActionsTopMarginPx
- mDp.overviewActionsHeight;
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 5b40849..e8b46d5 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -453,6 +453,7 @@
protected final Rect mLastComputedTaskSize = new Rect();
protected final Rect mLastComputedGridSize = new Rect();
protected final Rect mLastComputedGridTaskSize = new Rect();
+ private TaskView mSelectedTask = null;
// How much a task that is directly offscreen will be pushed out due to RecentsView scale/pivot.
@Nullable
protected Float mLastComputedTaskStartPushOutDistance = null;
@@ -1084,6 +1085,16 @@
super.draw(canvas);
}
+ @Override
+ public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+ if (isModal()) {
+ // Do not scroll when clicking on a modal grid task, as it will already be centered
+ // on screen.
+ return false;
+ }
+ return super.requestChildRectangleOnScreen(child, rectangle, immediate);
+ }
+
public void addSideTaskLaunchCallback(RunnableList callback) {
if (mSideTaskLaunchCallback == null) {
mSideTaskLaunchCallback = new RunnableList();
@@ -1272,7 +1283,7 @@
*/
@Nullable
public TaskView getTaskViewByTaskId(int taskId) {
- if (taskId == -1) {
+ if (taskId == INVALID_TASK_ID) {
return null;
}
@@ -1964,7 +1975,7 @@
private void onOrientationChanged() {
// If overview is in modal state when rotate, reset it to overview state without running
// animation.
- setModalStateEnabled(/* isModalState= */ false, /* animate= */ false);
+ setModalStateEnabled(/* taskId= */ INVALID_TASK_ID, /* animate= */ false);
if (isSplitSelectionActive()) {
onRotateInSplitSelectionState();
}
@@ -2037,12 +2048,33 @@
}
/**
- * Returns the size of task selected to enter modal state.
+ * Sets the last TaskView selected.
*/
- public Point getSelectedTaskSize() {
- mSizeStrategy.calculateTaskSize(mActivity, mActivity.getDeviceProfile(),
- mTempRect);
- return new Point(mTempRect.width(), mTempRect.height());
+ public void setSelectedTask(int lastSelectedTaskId) {
+ mSelectedTask = getTaskViewByTaskId(lastSelectedTaskId);
+ }
+
+ /**
+ * Returns the bounds of the task selected to enter modal state.
+ */
+ public Rect getSelectedTaskBounds() {
+ if (mSelectedTask == null) {
+ return mLastComputedTaskSize;
+ }
+ return getTaskBounds(mSelectedTask);
+ }
+
+ private Rect getTaskBounds(TaskView taskView) {
+ int selectedPage = indexOfChild(taskView);
+ int primaryScroll = mOrientationHandler.getPrimaryScroll(this);
+ int selectedPageScroll = getScrollForPage(selectedPage);
+ boolean isTopRow = taskView != null && mTopRowIdSet.contains(taskView.getTaskViewId());
+ Rect outRect = new Rect(mLastComputedTaskSize);
+ outRect.offset(
+ -(primaryScroll - (selectedPageScroll + getOffsetFromScrollPosition(selectedPage))),
+ (int) (showAsGrid() && ENABLE_GRID_ONLY_OVERVIEW.get() && !isTopRow
+ ? mTopBottomRowHeightDiff : 0));
+ return outRect;
}
/** Gets the last computed task size */
@@ -4166,8 +4198,13 @@
private void updatePivots() {
if (mOverviewSelectEnabled) {
- setPivotX(mLastComputedTaskSize.centerX());
- setPivotY(mLastComputedTaskSize.bottom);
+ getModalTaskSize(mTempRect);
+ Rect selectedTaskPosition = getSelectedTaskBounds();
+
+ Utilities.getPivotsForScalingRectToRect(mTempRect, selectedTaskPosition,
+ mTempPointF);
+ setPivotX(mTempPointF.x);
+ setPivotY(mTempPointF.y);
} else {
getPagedViewOrientedState().getFullScreenScaleAndPivot(mTempRect,
mActivity.getDeviceProfile(), mTempPointF);
@@ -4180,11 +4217,17 @@
float offset = mAdjacentPageHorizontalOffset;
float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness);
int count = getChildCount();
+ boolean showAsGrid = showAsGrid();
TaskView runningTask = mRunningTaskViewId == -1 || !mRunningTaskTileHidden
? null : getRunningTaskView();
int midpoint = runningTask == null ? -1 : indexOfChild(runningTask);
int modalMidpoint = getCurrentPage();
+ boolean isModalGridWithoutFocusedTask =
+ showAsGrid && ENABLE_GRID_ONLY_OVERVIEW.get() && mTaskModalness > 0;
+ if (isModalGridWithoutFocusedTask) {
+ modalMidpoint = indexOfChild(mSelectedTask);
+ }
float midpointOffsetSize = 0;
float leftOffsetSize = midpoint - 1 >= 0
@@ -4194,7 +4237,6 @@
? getHorizontalOffsetSize(midpoint + 1, midpoint, offset)
: 0;
- boolean showAsGrid = showAsGrid();
float modalMidpointOffsetSize = 0;
float modalLeftOffsetSize = 0;
float modalRightOffsetSize = 0;
@@ -4222,23 +4264,34 @@
: i < midpoint
? leftOffsetSize
: rightOffsetSize;
+ if (isModalGridWithoutFocusedTask) {
+ gridOffsetSize = getHorizontalOffsetSize(i, modalMidpoint, modalOffset);
+ gridOffsetSize = Math.abs(gridOffsetSize) * (i <= modalMidpoint ? 1 : -1);
+ }
float modalTranslation = i == modalMidpoint
? modalMidpointOffsetSize
: showAsGrid
? gridOffsetSize
: i < modalMidpoint ? modalLeftOffsetSize : modalRightOffsetSize;
- float totalTranslation = translation + modalTranslation;
+ float totalTranslationX = translation + modalTranslation;
View child = getChildAt(i);
- FloatProperty translationProperty = child instanceof TaskView
+ FloatProperty translationPropertyX = child instanceof TaskView
? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
: mOrientationHandler.getPrimaryViewTranslate();
- translationProperty.set(child, totalTranslation);
+ translationPropertyX.set(child, totalTranslationX);
if (mEnableDrawingLiveTile && i == getRunningTaskIndex()) {
runActionOnRemoteHandles(
remoteTargetHandle -> remoteTargetHandle.getTaskViewSimulator()
- .taskPrimaryTranslation.value = totalTranslation);
+ .taskPrimaryTranslation.value = totalTranslationX);
redrawLiveTile();
}
+
+ if (showAsGrid && ENABLE_GRID_ONLY_OVERVIEW.get() && child instanceof TaskView) {
+ float totalTranslationY = getVerticalOffsetSize(i, modalOffset);
+ FloatProperty translationPropertyY =
+ ((TaskView) child).getSecondaryTaskOffsetTranslationProperty();
+ translationPropertyY.set(child, totalTranslationY);
+ }
}
updateCurveProperties();
}
@@ -4336,6 +4389,38 @@
return distanceToOffscreen * offsetProgress;
}
+ /**
+ * Computes the vertical distance to offset a given child such that it is completely offscreen.
+ *
+ * @param offsetProgress From 0 to 1 where 0 means no offset and 1 means offset offscreen.
+ */
+ private float getVerticalOffsetSize(int childIndex, float offsetProgress) {
+ if (offsetProgress == 0 || !(showAsGrid() && ENABLE_GRID_ONLY_OVERVIEW.get())
+ || mSelectedTask == null) {
+ // Don't bother calculating everything below if we won't offset vertically.
+ return 0;
+ }
+
+ // First, get the position of the task relative to the top row.
+ TaskView child = getTaskViewAt(childIndex);
+ Rect taskPosition = getTaskBounds(child);
+
+ boolean isSelectedTaskTopRow = mTopRowIdSet.contains(mSelectedTask.getTaskViewId());
+ boolean isChildTopRow = mTopRowIdSet.contains(child.getTaskViewId());
+ // Whether the task should be shifted to the top.
+ boolean isTopShift = !isSelectedTaskTopRow && isChildTopRow;
+ boolean isBottomShift = isSelectedTaskTopRow && !isChildTopRow;
+
+ // Next, calculate the distance to move the task off screen at scale = 1.
+ float distanceToOffscreen = 0;
+ if (isTopShift) {
+ distanceToOffscreen = -taskPosition.bottom;
+ } else if (isBottomShift) {
+ distanceToOffscreen = mActivity.getDeviceProfile().heightPx - taskPosition.top;
+ }
+ return distanceToOffscreen * offsetProgress;
+ }
+
protected void setTaskViewsResistanceTranslation(float translation) {
mTaskViewsSecondaryTranslation = translation;
for (int i = 0; i < getTaskViewCount(); i++) {
@@ -4435,9 +4520,8 @@
* Resets the visuals when exit modal state.
*/
public void resetModalVisuals() {
- TaskView taskView = getCurrentPageTaskView();
- if (taskView != null) {
- taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
+ if (mSelectedTask != null) {
+ mSelectedTask.getThumbnail().getTaskOverlay().resetModalVisuals();
}
}
@@ -5439,6 +5523,9 @@
if (mOverviewSelectEnabled != overviewSelectEnabled) {
mOverviewSelectEnabled = overviewSelectEnabled;
updatePivots();
+ if (!mOverviewSelectEnabled) {
+ setSelectedTask(INVALID_TASK_ID);
+ }
}
}
@@ -5509,7 +5596,9 @@
private void setTaskModalness(float modalness) {
mTaskModalness = modalness;
updatePageOffsets();
- if (getCurrentPageTaskView() != null) {
+ if (mSelectedTask != null) {
+ mSelectedTask.setModalness(modalness);
+ } else if (getCurrentPageTaskView() != null) {
getCurrentPageTaskView().setModalness(modalness);
}
// Only show actions view when it's modal for in-place landscape mode.
@@ -5524,7 +5613,7 @@
}
/** Enables or disables modal state for RecentsView */
- public abstract void setModalStateEnabled(boolean isModalState, boolean animate);
+ public abstract void setModalStateEnabled(int taskId, boolean animate);
public TaskOverlayFactory getTaskOverlayFactory() {
return mTaskOverlayFactory;
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d3c7778..35e4a76 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -1387,6 +1387,11 @@
TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
}
+ public FloatProperty<TaskView> getSecondaryTaskOffsetTranslationProperty() {
+ return getPagedOrientationHandler().getSecondaryValue(
+ TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+ }
+
public FloatProperty<TaskView> getTaskResistanceTranslationProperty() {
return getPagedOrientationHandler().getSecondaryValue(
TASK_RESISTANCE_TRANSLATION_X, TASK_RESISTANCE_TRANSLATION_Y);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index bd9493b..7011f58 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -38,6 +38,7 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
@@ -343,6 +344,21 @@
}
/**
+ * Sets the x and y pivots for scaling from one Rect to another.
+ *
+ * @param src the source rectangle to scale from.
+ * @param dst the destination rectangle to scale to.
+ * @param outPivot the pivots set for scaling from src to dst.
+ */
+ public static void getPivotsForScalingRectToRect(Rect src, Rect dst, PointF outPivot) {
+ float pivotXPct = ((float) src.left - dst.left) / ((float) dst.width() - src.width());
+ outPivot.x = dst.left + dst.width() * pivotXPct;
+
+ float pivotYPct = ((float) src.top - dst.top) / ((float) dst.height() - src.height());
+ outPivot.y = dst.top + dst.height() * pivotYPct;
+ }
+
+ /**
* Maps t from one range to another range.
* @param t The value to map.
* @param fromMin The lower bound of the range that t is being mapped from.