Merge "Animate scrolling grid into place when there is a gap between last tasks and clear all." into sc-v2-dev
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
index b9a9006..22c87b0 100644
--- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -39,10 +39,24 @@
}
};
+ public static final FloatProperty<ClearAllButton> DISMISS_ALPHA =
+ new FloatProperty<ClearAllButton>("dismissAlpha") {
+ @Override
+ public Float get(ClearAllButton view) {
+ return view.mDismissAlpha;
+ }
+
+ @Override
+ public void setValue(ClearAllButton view, float v) {
+ view.setDismissAlpha(v);
+ }
+ };
+
private final StatefulActivity mActivity;
private float mScrollAlpha = 1;
private float mContentAlpha = 1;
private float mVisibilityAlpha = 1;
+ private float mDismissAlpha = 1;
private float mFullscreenProgress = 1;
private float mGridProgress = 1;
@@ -97,6 +111,13 @@
}
}
+ public void setDismissAlpha(float alpha) {
+ if (mDismissAlpha != alpha) {
+ mDismissAlpha = alpha;
+ updateAlpha();
+ }
+ }
+
public void onRecentsViewScroll(int scroll, boolean gridEnabled) {
RecentsView recentsView = getRecentsView();
if (recentsView == null) {
@@ -123,7 +144,7 @@
}
private void updateAlpha() {
- final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha;
+ final float alpha = mScrollAlpha * mContentAlpha * mVisibilityAlpha * mDismissAlpha;
setAlpha(alpha);
setClickable(Math.min(alpha, 1) == 1);
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 78b3d4f..8c37644 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -29,6 +29,7 @@
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.QuickstepTransitionManager.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+import static com.android.launcher3.Utilities.boundToRange;
import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
@@ -49,6 +50,7 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NON_ZERO_ROTATION;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_RECENTS;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_NO_TASKS;
@@ -359,6 +361,7 @@
private static final float INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.55f;
private static final float ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.05f;
private static final float ANIMATION_DISMISS_PROGRESS_MIDPOINT = 0.5f;
+ private static final float END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.75f;
private static final float SIGNIFICANT_MOVE_THRESHOLD_TABLET = 0.15f;
@@ -1004,6 +1007,43 @@
}
}
+ private boolean isLastGridTaskVisible() {
+ TaskView lastTaskView = getLastGridTaskView();
+ return lastTaskView != null && lastTaskView.isVisibleToUser();
+ }
+
+ private TaskView getLastGridTaskView() {
+ IntArray topRowIdArray = getTopRowIdArray();
+ IntArray bottomRowIdArray = getBottomRowIdArray();
+ if (topRowIdArray.isEmpty() && bottomRowIdArray.isEmpty()) {
+ return null;
+ }
+ int lastTaskViewId = topRowIdArray.size() >= bottomRowIdArray.size() ? topRowIdArray.get(
+ topRowIdArray.size() - 1) : bottomRowIdArray.get(bottomRowIdArray.size() - 1);
+ return getTaskViewFromTaskViewId(lastTaskViewId);
+ }
+
+ private int getSnapToLastTaskScrollDiff() {
+ // Snap to a position where ClearAll is just invisible.
+ int screenStart = mOrientationHandler.getPrimaryScroll(this);
+ int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+ int clearAllScroll = getScrollForPage(indexOfChild(mClearAllButton));
+ int targetScroll = clearAllScroll + (mIsRtl ? clearAllWidth : -clearAllWidth);
+ return screenStart - targetScroll;
+ }
+
+ private int getSnapToFocusedTaskScrollDiff(boolean isClearAllHidden) {
+ int screenStart = mOrientationHandler.getPrimaryScroll(this);
+ int targetScroll = getScrollForPage(indexOfChild(getFocusedTaskView()));
+ if (!isClearAllHidden) {
+ int clearAllWidth = mOrientationHandler.getPrimarySize(mClearAllButton);
+ int taskGridHorizontalDiff = mLastComputedTaskSize.right - mLastComputedGridSize.right;
+ int clearAllFocusScrollDiff = taskGridHorizontalDiff - clearAllWidth;
+ targetScroll += mIsRtl ? clearAllFocusScrollDiff : -clearAllFocusScrollDiff;
+ }
+ return screenStart - targetScroll;
+ }
+
private boolean isTaskViewWithinBounds(TaskView tv, int start, int end) {
int taskStart = mOrientationHandler.getChildStart(tv) + (int) tv.getOffsetAdjustment(
showAsFullscreen(), showAsGrid());
@@ -1083,7 +1123,6 @@
super.onPageEndTransition();
if (isClearAllHidden()) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
- } else {
}
if (getNextPage() > 0) {
setSwipeDownShouldLaunchApp(true);
@@ -2678,6 +2717,94 @@
}
}
+ float dismissTranslationInterpolationEnd = 1;
+ boolean closeGapBetweenClearAll = false;
+ boolean isClearAllHidden = isClearAllHidden();
+ if (showAsGrid && isLastGridTaskVisible()) {
+ // After dismissal, animate translation of the remaining tasks to fill any gap left
+ // between the end of the grid and the clear all button. Only animate if the clear
+ // all button is visible or would become visible after dismissal.
+ float longGridRowWidthDiff = 0;
+
+ int topGridRowSize = mTopRowIdSet.size();
+ int bottomGridRowSize = taskCount - mTopRowIdSet.size() - 1;
+ boolean topRowLonger = topGridRowSize > bottomGridRowSize;
+ boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
+ boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
+ boolean dismissedTaskFromBottom = !dismissedTaskFromTop && !isFocusedTaskDismissed;
+ float gapWidth = 0;
+ if ((topRowLonger && dismissedTaskFromTop)
+ || (bottomRowLonger && dismissedTaskFromBottom)) {
+ gapWidth = dismissedTaskWidth;
+ } else if ((topRowLonger && nextFocusedTaskFromTop)
+ || (bottomRowLonger && !nextFocusedTaskFromTop)) {
+ gapWidth = nextFocusedTaskWidth;
+ }
+ if (gapWidth > 0) {
+ if (taskCount > 2) {
+ // Compensate the removed gap.
+ longGridRowWidthDiff += mIsRtl ? -gapWidth : gapWidth;
+ if (isClearAllHidden) {
+ // If ClearAllButton isn't fully shown, snap to the last task.
+ longGridRowWidthDiff += getSnapToLastTaskScrollDiff();
+ }
+ } else {
+ // If only focused task will be left, snap to focused task instead.
+ longGridRowWidthDiff += getSnapToFocusedTaskScrollDiff(isClearAllHidden);
+ }
+ }
+
+ // If we need to animate the grid to compensate the clear all gap, we split the second
+ // half of the dismiss pending animation (in which the non-dismissed tasks slide into
+ // place) in half again, making the first quarter the existing non-dismissal sliding
+ // and the second quarter this new animation of gap filling. This is due to the fact
+ // that PendingAnimation is a single animation, not a sequence of animations, so we
+ // fake it using interpolation.
+ if (longGridRowWidthDiff != 0) {
+ closeGapBetweenClearAll = true;
+ // Stagger the offsets of each additional task for a delayed animation. We use
+ // half here as this animation is half of half of an animation (1/4th).
+ float halfAdditionalDismissTranslationOffset =
+ (0.5f * ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET);
+ dismissTranslationInterpolationEnd = Utilities.boundToRange(
+ END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ + (taskCount - 1) * halfAdditionalDismissTranslationOffset,
+ END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
+ for (int i = 0; i < taskCount; i++) {
+ TaskView taskView = getTaskViewAt(i);
+ anim.setFloat(taskView, TaskView.GRID_END_TRANSLATION_X, longGridRowWidthDiff,
+ clampToProgress(LINEAR, dismissTranslationInterpolationEnd, 1));
+ dismissTranslationInterpolationEnd = Utilities.boundToRange(
+ dismissTranslationInterpolationEnd
+ - halfAdditionalDismissTranslationOffset,
+ END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+ && taskView.isRunningTask()) {
+ anim.addOnFrameCallback(() -> {
+ runActionOnRemoteHandles(
+ remoteTargetHandle ->
+ remoteTargetHandle.mTaskViewSimulator
+ .taskPrimaryTranslation.value =
+ TaskView.GRID_END_TRANSLATION_X.get(taskView));
+ redrawLiveTile();
+ });
+ }
+ }
+
+ // Change alpha of clear all if translating grid to hide it
+ if (isClearAllHidden) {
+ anim.setFloat(mClearAllButton, DISMISS_ALPHA, 0, LINEAR);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mClearAllButton.setDismissAlpha(1);
+ }
+ });
+ }
+ }
+ }
+
int distanceFromDismissedTask = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
@@ -2757,22 +2884,25 @@
float animationStartProgress = Utilities.boundToRange(
INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
- * ++distanceFromDismissedTask, 0f, 1f);
+ * ++distanceFromDismissedTask, 0f,
+ dismissTranslationInterpolationEnd);
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
anim.setFloat(taskView, TaskView.SNAPSHOT_SCALE, scale,
- clampToProgress(LINEAR, animationStartProgress, 1f));
+ clampToProgress(LINEAR, animationStartProgress,
+ dismissTranslationInterpolationEnd));
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? dismissedTaskWidth : -dismissedTaskWidth,
- clampToProgress(LINEAR, animationStartProgress, 1f));
+ clampToProgress(LINEAR, animationStartProgress,
+ dismissTranslationInterpolationEnd));
float secondaryTranslation = -mTaskGridVerticalDiff;
if (!nextFocusedTaskFromTop) {
secondaryTranslation -= mTopBottomRowHeightDiff;
}
anim.setFloat(taskView, taskView.getSecondaryDissmissTranslationProperty(),
- secondaryTranslation,
- clampToProgress(LINEAR, animationStartProgress, 1f));
+ secondaryTranslation, clampToProgress(LINEAR, animationStartProgress,
+ dismissTranslationInterpolationEnd));
anim.setFloat(taskView, TaskView.FOCUS_TRANSITION, 0f,
clampToProgress(LINEAR, 0f, ANIMATION_DISMISS_PROGRESS_MIDPOINT));
} else {
@@ -2780,7 +2910,8 @@
isFocusedTaskDismissed ? nextFocusedTaskWidth : dismissedTaskWidth;
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? primaryTranslation : -primaryTranslation,
- clampToProgress(LINEAR, animationStartProgress, 1f));
+ clampToProgress(LINEAR, animationStartProgress,
+ dismissTranslationInterpolationEnd));
}
}
}
@@ -2796,6 +2927,7 @@
mPendingAnimation = anim;
final TaskView finalNextFocusedTaskView = nextFocusedTaskView;
+ final boolean finalCloseGapBetweenClearAll = closeGapBetweenClearAll;
mPendingAnimation.addEndListener(new Consumer<Boolean>() {
@Override
public void accept(Boolean success) {
@@ -2831,49 +2963,68 @@
resetTaskVisuals();
int pageToSnapTo = mCurrentPage;
+ mCurrentPageScrollDiff = 0;
int taskViewIdToSnapTo = -1;
if (showAsGrid) {
- // Get the id of the task view we will snap to based on the current
- // page's relative position as the order of indices change over time due
- // to dismissals.
- TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(mCurrentPage);
- if (snappedTaskView != null) {
- if (snappedTaskView.getTaskViewId() == mFocusedTaskViewId) {
- if (finalNextFocusedTaskView != null) {
- taskViewIdToSnapTo = finalNextFocusedTaskView.getTaskViewId();
- } else {
- taskViewIdToSnapTo = mFocusedTaskViewId;
+ if (finalCloseGapBetweenClearAll) {
+ if (taskCount > 2) {
+ pageToSnapTo = indexOfChild(mClearAllButton);
+ if (isClearAllHidden) {
+ int clearAllWidth = mOrientationHandler.getPrimarySize(
+ mClearAllButton);
+ mCurrentPageScrollDiff =
+ isRtl() ? clearAllWidth : -clearAllWidth;
}
- } else {
- int snappedTaskViewId = snappedTaskView.getTaskViewId();
- boolean isSnappedTaskInTopRow = mTopRowIdSet.contains(
- snappedTaskViewId);
- IntArray taskViewIdArray =
- isSnappedTaskInTopRow ? getTopRowIdArray()
- : getBottomRowIdArray();
- int snappedIndex = taskViewIdArray.indexOf(snappedTaskViewId);
- taskViewIdArray.removeValue(dismissedTaskViewId);
- if (snappedIndex < taskViewIdArray.size()) {
- taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
- } else if (snappedIndex == taskViewIdArray.size()) {
- // If the snapped task is the last item from the dismissed row,
- // snap to the same column in the other grid row
- IntArray inverseRowTaskViewIdArray =
- isSnappedTaskInTopRow ? getBottomRowIdArray()
- : getTopRowIdArray();
- if (snappedIndex < inverseRowTaskViewIdArray.size()) {
- taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
- snappedIndex);
+ } else if (isClearAllHidden) {
+ // Snap to focused task if clear all is hidden.
+ pageToSnapTo = 0;
+ }
+ } else {
+ // Get the id of the task view we will snap to based on the current
+ // page's relative position as the order of indices change over time due
+ // to dismissals.
+ TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(mCurrentPage);
+ if (snappedTaskView != null) {
+ if (snappedTaskView.getTaskViewId() == mFocusedTaskViewId) {
+ if (finalNextFocusedTaskView != null) {
+ taskViewIdToSnapTo =
+ finalNextFocusedTaskView.getTaskViewId();
+ } else {
+ taskViewIdToSnapTo = mFocusedTaskViewId;
+ }
+ } else {
+ int snappedTaskViewId = snappedTaskView.getTaskViewId();
+ boolean isSnappedTaskInTopRow = mTopRowIdSet.contains(
+ snappedTaskViewId);
+ IntArray taskViewIdArray =
+ isSnappedTaskInTopRow ? getTopRowIdArray()
+ : getBottomRowIdArray();
+ int snappedIndex = taskViewIdArray.indexOf(snappedTaskViewId);
+ taskViewIdArray.removeValue(dismissedTaskViewId);
+ if (snappedIndex < taskViewIdArray.size()) {
+ taskViewIdToSnapTo = taskViewIdArray.get(snappedIndex);
+ } else if (snappedIndex == taskViewIdArray.size()) {
+ // If the snapped task is the last item from the
+ // dismissed row,
+ // snap to the same column in the other grid row
+ IntArray inverseRowTaskViewIdArray =
+ isSnappedTaskInTopRow ? getBottomRowIdArray()
+ : getTopRowIdArray();
+ if (snappedIndex < inverseRowTaskViewIdArray.size()) {
+ taskViewIdToSnapTo = inverseRowTaskViewIdArray.get(
+ snappedIndex);
+ }
}
}
}
- }
- int primaryScroll = mOrientationHandler.getPrimaryScroll(RecentsView.this);
- int currentPageScroll = getScrollForPage(pageToSnapTo);
- mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+ int primaryScroll = mOrientationHandler.getPrimaryScroll(
+ RecentsView.this);
+ int currentPageScroll = getScrollForPage(pageToSnapTo);
+ mCurrentPageScrollDiff = primaryScroll - currentPageScroll;
+ }
} else if (dismissedIndex < pageToSnapTo || pageToSnapTo == taskCount - 1) {
- pageToSnapTo -= 1;
+ pageToSnapTo--;
}
removeViewInLayout(dismissedTaskView);
mTopRowIdSet.remove(dismissedTaskViewId);
@@ -2938,7 +3089,13 @@
}
}
setCurrentPage(pageToSnapTo);
+ // Update various scroll depedent UI.
dispatchScrollChanged();
+ updateActionsViewScrollAlpha();
+ if (isClearAllHidden()) {
+ mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING,
+ false);
+ }
}
}
updateFocusedSplitButtonVisibility();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 80f5dc4..8a20da6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -343,6 +343,19 @@
}
};
+ public static final FloatProperty<TaskView> GRID_END_TRANSLATION_X =
+ new FloatProperty<TaskView>("gridEndTranslationX") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setGridEndTranslationX(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mGridEndTranslationX;
+ }
+ };
+
public static final FloatProperty<TaskView> SNAPSHOT_SCALE =
new FloatProperty<TaskView>("snapshotScale") {
@Override
@@ -381,6 +394,8 @@
// The following grid translations scales with mGridProgress.
private float mGridTranslationX;
private float mGridTranslationY;
+ // The following grid translation is used to animate closing the gap between grid and clear all.
+ private float mGridEndTranslationX;
// Applied as a complement to gridTranslation, for adjusting the carousel overview and quick
// switch.
private float mNonGridTranslationX;
@@ -950,8 +965,8 @@
protected void resetViewTransforms() {
// fullscreenTranslation and accumulatedTranslation should not be reset, as
// resetViewTransforms is called during Quickswitch scrolling.
- mDismissTranslationX = mTaskOffsetTranslationX = mTaskResistanceTranslationX =
- mSplitSelectTranslationX = 0f;
+ mDismissTranslationX = mTaskOffsetTranslationX =
+ mTaskResistanceTranslationX = mSplitSelectTranslationX = mGridEndTranslationX = 0f;
mDismissTranslationY = mTaskOffsetTranslationY = mTaskResistanceTranslationY =
mSplitSelectTranslationY = 0f;
setSnapshotScale(1f);
@@ -1162,6 +1177,11 @@
return mGridTranslationY;
}
+ private void setGridEndTranslationX(float gridEndTranslationX) {
+ mGridEndTranslationX = gridEndTranslationX;
+ applyTranslationX();
+ }
+
public float getScrollAdjustment(boolean fullscreenEnabled, boolean gridEnabled) {
float scrollAdjustment = 0;
if (gridEnabled) {
@@ -1191,7 +1211,7 @@
private void applyTranslationX() {
setTranslationX(mDismissTranslationX + mTaskOffsetTranslationX + mTaskResistanceTranslationX
- + mSplitSelectTranslationX + getPersistentTranslationX());
+ + mSplitSelectTranslationX + mGridEndTranslationX + getPersistentTranslationX());
}
private void applyTranslationY() {