Merge "Translate grid end for tasks after dismiss." into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
22a61d32ca
+2
-1
@@ -246,7 +246,8 @@ public class TaskViewTouchControllerDeprecated<
|
||||
pa = new PendingAnimation(maxDuration);
|
||||
mRecentsView.createTaskDismissAnimation(pa, mTaskBeingDragged,
|
||||
true /* animateTaskView */, true /* removeTask */, maxDuration,
|
||||
false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
|
||||
false /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */,
|
||||
null /* gridEndData */);
|
||||
|
||||
mEndDisplacement = -secondaryTaskDimension;
|
||||
} else {
|
||||
|
||||
@@ -143,7 +143,7 @@ public class FallbackRecentsView<CONTAINER_TYPE extends Context & RecentsViewCon
|
||||
PendingAnimation pendingAnimation = new PendingAnimation(TASK_DISMISS_DURATION);
|
||||
createTaskDismissAnimation(pendingAnimation, homeTaskView, true, false,
|
||||
TASK_DISMISS_DURATION, false /* dismissingForSplitSelection*/,
|
||||
false /* isExpressiveDismiss */);
|
||||
false /* isExpressiveDismiss */, null /* gridEndData */);
|
||||
pendingAnimation.addEndListener(e -> setCurrentTask(-1));
|
||||
AnimatorPlaybackController controller = pendingAnimation.createPlaybackController();
|
||||
controller.dispatchOnStart();
|
||||
|
||||
@@ -29,9 +29,11 @@ import com.android.launcher3.util.MSDLPlayerWrapper
|
||||
import com.android.launcher3.views.ActivityContext
|
||||
import com.android.quickstep.util.TaskGridNavHelper
|
||||
import com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY
|
||||
import com.android.quickstep.views.TaskView.Companion.GRID_END_TRANSLATION_X
|
||||
import com.google.android.msdl.data.model.MSDLToken
|
||||
import com.google.android.msdl.domain.InteractionProperties
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
|
||||
@@ -343,6 +345,15 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
|
||||
)
|
||||
}
|
||||
|
||||
private fun createExpressiveDismissAlphaSpringForce(): SpringForce {
|
||||
val resourceProvider = DynamicResource.provider(recentsView.mContainer)
|
||||
return SpringForce()
|
||||
.setDampingRatio(
|
||||
resourceProvider.getFloat(R.dimen.expressive_dismiss_effects_damping_ratio)
|
||||
)
|
||||
.setStiffness(resourceProvider.getFloat(R.dimen.expressive_dismiss_effects_stiffness))
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays a haptic as the dragged task view settles back into its rest state.
|
||||
*
|
||||
@@ -477,16 +488,11 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
|
||||
}
|
||||
}
|
||||
|
||||
// Start animations and remove the dismissed task at the end, dismiss immediately if no
|
||||
// neighboring tasks exist.
|
||||
val runGridEndAnimationAndRelayout = { dismissDuration: Int ->
|
||||
recentsView.expressiveDismissTaskView(dismissedTaskView, onEndRunnable, dismissDuration)
|
||||
}
|
||||
val runImmediately = tasksToReflow.isEmpty()
|
||||
if (runImmediately) {
|
||||
// Play the same haptic as when neighbors spring into place.
|
||||
MSDLPlayerWrapper.INSTANCE.get(recentsView.context)?.playToken(MSDLToken.CANCEL)
|
||||
runGridEndAnimationAndRelayout(DISMISS_IMMEDIATE_DURATION)
|
||||
runGridEndTranslation(dismissedTaskView, onEndRunnable, DISMISS_IMMEDIATE_DURATION)
|
||||
} else {
|
||||
addNeighborSettlingSpringAnimations(
|
||||
dismissedTaskView,
|
||||
@@ -498,7 +504,11 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
|
||||
)
|
||||
springAnimationDriver.apply {
|
||||
addEndListener { _, _, _, _ ->
|
||||
runGridEndAnimationAndRelayout(DISMISS_DEFAULT_DURATION)
|
||||
runGridEndTranslation(
|
||||
dismissedTaskView,
|
||||
onEndRunnable,
|
||||
DISMISS_DEFAULT_DURATION,
|
||||
)
|
||||
}
|
||||
animateToFinalPosition(dismissedTaskGap)
|
||||
}
|
||||
@@ -587,11 +597,237 @@ class RecentsDismissUtils(private val recentsView: RecentsView<*, *>) {
|
||||
return lastTaskViewSpring
|
||||
}
|
||||
|
||||
/** Animates the grid to compensate the clear all gap after dismissal. */
|
||||
private fun runGridEndTranslation(
|
||||
dismissedTaskView: TaskView,
|
||||
onEndRunnable: () -> Unit,
|
||||
dismissDuration: Int,
|
||||
) {
|
||||
val runGridEndAnimationAndRelayout = { gridEndData: GridEndData ->
|
||||
recentsView.expressiveDismissTaskView(
|
||||
dismissedTaskView,
|
||||
onEndRunnable,
|
||||
dismissDuration,
|
||||
gridEndData,
|
||||
)
|
||||
}
|
||||
val gridEndData = getGridEndData(dismissedTaskView)
|
||||
val gridEndOffset = gridEndData.gridEndOffset
|
||||
if (gridEndOffset == 0f) {
|
||||
runGridEndAnimationAndRelayout(gridEndData)
|
||||
return
|
||||
}
|
||||
|
||||
// Create spring animation to drive all task grid translation simultaneously.
|
||||
val gridEndSpring =
|
||||
SpringAnimation(FloatValueHolder())
|
||||
.setSpring(createExpressiveGridReflowSpringForce(gridEndOffset))
|
||||
recentsView.mUtils.taskViews.forEach { taskView ->
|
||||
val taskViewGridEndSpringAnimation =
|
||||
SpringAnimation(
|
||||
taskView,
|
||||
FloatPropertyCompat.createFloatPropertyCompat(GRID_END_TRANSLATION_X),
|
||||
)
|
||||
.setSpring(createExpressiveGridReflowSpringForce(gridEndOffset))
|
||||
// Update live tile on spring animation.
|
||||
if (taskView.isRunningTask && recentsView.enableDrawingLiveTile) {
|
||||
taskViewGridEndSpringAnimation.addUpdateListener { _, _, _ ->
|
||||
recentsView.runActionOnRemoteHandles { remoteTargetHandle ->
|
||||
remoteTargetHandle.taskViewSimulator.taskPrimaryTranslation.value =
|
||||
GRID_END_TRANSLATION_X.get(taskView)
|
||||
}
|
||||
recentsView.redrawLiveTile()
|
||||
}
|
||||
}
|
||||
gridEndSpring.addUpdateListener { _, value, _ ->
|
||||
taskViewGridEndSpringAnimation.animateToFinalPosition(value)
|
||||
}
|
||||
}
|
||||
// Animate alpha of clear all if translating grid to hide it.
|
||||
if (recentsView.isClearAllHidden) {
|
||||
SpringAnimation(
|
||||
recentsView.clearAllButton,
|
||||
FloatPropertyCompat.createFloatPropertyCompat(ClearAllButton.DISMISS_ALPHA),
|
||||
)
|
||||
.setSpring(createExpressiveDismissAlphaSpringForce())
|
||||
.addEndListener { _, _, _, _ -> recentsView.clearAllButton.dismissAlpha = 1f }
|
||||
.animateToFinalPosition(0f)
|
||||
}
|
||||
gridEndSpring.addEndListener { _, _, _, _ -> runGridEndAnimationAndRelayout(gridEndData) }
|
||||
gridEndSpring.animateToFinalPosition(gridEndOffset)
|
||||
}
|
||||
|
||||
/** Returns the distance between the end of the grid and clear all button after dismissal. */
|
||||
fun getGridEndData(
|
||||
dismissedTaskView: TaskView?,
|
||||
isExpressiveDismiss: Boolean = true,
|
||||
isFocusedTaskDismissed: Boolean = false,
|
||||
nextFocusedTaskView: TaskView? = null,
|
||||
isStagingFocusedTask: Boolean = false,
|
||||
nextFocusedTaskFromTop: Boolean = false,
|
||||
nextFocusedTaskWidth: Float = 0f,
|
||||
): GridEndData {
|
||||
var gridEndOffset = 0f
|
||||
var snapToLastTask = false
|
||||
var newClearAllShortTotalWidthTranslation: Float
|
||||
var currentPageSnapsToEndOfGrid: Boolean
|
||||
with(recentsView) {
|
||||
val lastGridTaskView = if (showAsGrid()) lastGridTaskView else null
|
||||
val currentPageScroll = getScrollForPage(currentPage)
|
||||
val lastGridTaskScroll = getScrollForPage(indexOfChild(lastGridTaskView))
|
||||
currentPageSnapsToEndOfGrid = currentPageScroll == lastGridTaskScroll
|
||||
var topGridRowCount = mTopRowIdSet.size()
|
||||
var bottomGridRowCount =
|
||||
taskViewCount - mTopRowIdSet.size() - mUtils.getLargeTileCount()
|
||||
val topRowLonger = topGridRowCount > bottomGridRowCount
|
||||
val bottomRowLonger = bottomGridRowCount > topGridRowCount
|
||||
val dismissedFromTop =
|
||||
dismissedTaskView != null && mTopRowIdSet.contains(dismissedTaskView.taskViewId)
|
||||
val dismissedFromBottom =
|
||||
dismissedTaskView != null && !dismissedFromTop && !dismissedTaskView.isLargeTile
|
||||
if (dismissedFromTop || (isFocusedTaskDismissed && nextFocusedTaskFromTop)) {
|
||||
topGridRowCount--
|
||||
}
|
||||
if (dismissedFromBottom || (isFocusedTaskDismissed && !nextFocusedTaskFromTop)) {
|
||||
bottomGridRowCount--
|
||||
}
|
||||
newClearAllShortTotalWidthTranslation =
|
||||
getNewClearAllShortTotalWidthTranslation(
|
||||
topGridRowCount,
|
||||
bottomGridRowCount,
|
||||
isStagingFocusedTask,
|
||||
)
|
||||
val isLastGridTaskViewVisibleForDismiss =
|
||||
when {
|
||||
lastGridTaskView == null -> false
|
||||
isExpressiveDismiss ->
|
||||
isTaskViewVisible(lastGridTaskView) || lastGridTaskView == dismissedTaskView
|
||||
else -> lastGridTaskView.isVisibleToUser
|
||||
}
|
||||
if (!isLastGridTaskViewVisibleForDismiss) {
|
||||
return GridEndData(
|
||||
gridEndOffset,
|
||||
snapToLastTask,
|
||||
newClearAllShortTotalWidthTranslation,
|
||||
currentPageSnapsToEndOfGrid,
|
||||
)
|
||||
}
|
||||
val dismissedTaskWidth =
|
||||
if (dismissedTaskView == null) 0f
|
||||
else (dismissedTaskView.layoutParams.width + pageSpacing).toFloat()
|
||||
val gapWidth =
|
||||
when {
|
||||
(topRowLonger && dismissedFromTop) ||
|
||||
(bottomRowLonger && dismissedFromBottom) -> dismissedTaskWidth
|
||||
nextFocusedTaskView != null &&
|
||||
((topRowLonger && nextFocusedTaskFromTop) ||
|
||||
(bottomRowLonger && !nextFocusedTaskFromTop)) -> nextFocusedTaskWidth
|
||||
else -> 0f
|
||||
}
|
||||
if (gapWidth > 0) {
|
||||
if (clearAllShortTotalWidthTranslation == 0) {
|
||||
val gapCompensation = gapWidth - newClearAllShortTotalWidthTranslation
|
||||
gridEndOffset += if (isRtl) -gapCompensation else gapCompensation
|
||||
}
|
||||
if (isClearAllHidden) {
|
||||
// If ClearAllButton isn't fully shown, snap to the last task.
|
||||
snapToLastTask = true
|
||||
}
|
||||
}
|
||||
val isLeftRightSplit =
|
||||
(mContainer as ActivityContext).getDeviceProfile().isLeftRightSplit &&
|
||||
isSplitSelectionActive
|
||||
if (isLeftRightSplit && !isStagingFocusedTask) {
|
||||
// LastTask's scroll is the minimum scroll in split select, if current scroll is
|
||||
// beyond that, we'll need to snap to last task instead.
|
||||
getLastGridTaskView()?.let { lastTask ->
|
||||
val primaryScroll = pagedOrientationHandler.getPrimaryScroll(this)
|
||||
val lastTaskScroll = getScrollForPage(indexOfChild(lastTask))
|
||||
if (
|
||||
(isRtl && primaryScroll < lastTaskScroll) ||
|
||||
(!isRtl && primaryScroll > lastTaskScroll)
|
||||
) {
|
||||
snapToLastTask = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (snapToLastTask) {
|
||||
gridEndOffset += snapToLastTaskScrollDiff.toFloat()
|
||||
} else if (isLeftRightSplit && currentPageSnapsToEndOfGrid) {
|
||||
// Use last task as reference point for scroll diff and snapping calculation as it's
|
||||
// the only invariant point in landscape split screen.
|
||||
snapToLastTask = true
|
||||
}
|
||||
|
||||
// Handle large tile scroll when dismissing the last small task.
|
||||
if (mUtils.getGridTaskCount() == 1 && dismissedTaskView?.isGridTask == true) {
|
||||
mUtils.getLastLargeTaskView()?.let { lastLargeTile ->
|
||||
val primaryScroll = pagedOrientationHandler.getPrimaryScroll(this)
|
||||
val lastLargeTileScroll = getScrollForPage(indexOfChild(lastLargeTile))
|
||||
gridEndOffset = (primaryScroll - lastLargeTileScroll).toFloat()
|
||||
|
||||
if (!isClearAllHidden) {
|
||||
// If ClearAllButton is visible, reduce the distance by scroll difference
|
||||
// between ClearAllButton and the last task.
|
||||
gridEndOffset +=
|
||||
getLastTaskScroll(
|
||||
/*clearAllScroll=*/ 0,
|
||||
pagedOrientationHandler.getPrimarySize(clearAllButton),
|
||||
)
|
||||
.toFloat()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return GridEndData(
|
||||
gridEndOffset,
|
||||
snapToLastTask,
|
||||
newClearAllShortTotalWidthTranslation,
|
||||
currentPageSnapsToEndOfGrid,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getNewClearAllShortTotalWidthTranslation(
|
||||
topGridRowCount: Int,
|
||||
bottomGridRowCount: Int,
|
||||
isStagingFocusedTask: Boolean,
|
||||
): Float {
|
||||
with(recentsView) {
|
||||
if (clearAllShortTotalWidthTranslation != 0) {
|
||||
return 0f
|
||||
}
|
||||
// If first task is not in the expected position (mLastComputedTaskSize) and too
|
||||
// close to ClearAllButton, then apply extra translation to ClearAllButton.
|
||||
var longRowWidth =
|
||||
max(topGridRowCount, bottomGridRowCount) *
|
||||
(mLastComputedGridTaskSize.width() + pageSpacing)
|
||||
if (!enableGridOnlyOverview() && !isStagingFocusedTask) {
|
||||
longRowWidth += mLastComputedTaskSize.width() + pageSpacing
|
||||
}
|
||||
val firstTaskStart = mLastComputedGridSize.left + longRowWidth
|
||||
val expectedFirstTaskStart = mLastComputedTaskSize.right
|
||||
// Compensate the removed gap if we don't already have shortTotalCompensation,
|
||||
// and adjust accordingly to the new shortTotalCompensation after dismiss.
|
||||
return if (firstTaskStart < expectedFirstTaskStart) {
|
||||
(expectedFirstTaskStart - firstTaskStart).toFloat()
|
||||
} else {
|
||||
0f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class GridEndData(
|
||||
val gridEndOffset: Float,
|
||||
val snapToLastTask: Boolean,
|
||||
val newClearAllShortTotalWidthTranslation: Float,
|
||||
val currentPageSnapsToEndOfGrid: Boolean,
|
||||
)
|
||||
|
||||
private companion object {
|
||||
// The additional damping to apply to tasks further from the dismissed task.
|
||||
private const val ADDITIONAL_DISMISS_DAMPING_RATIO = 0.15f
|
||||
private const val RECENTS_SCALE_SPRING_MULTIPLIER = 1000f
|
||||
private const val DISMISS_DEFAULT_DURATION = 300
|
||||
private const val DISMISS_IMMEDIATE_DURATION = 10
|
||||
private const val DISMISS_IMMEDIATE_DURATION = 100
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1518,7 +1518,7 @@ public abstract class RecentsView<
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TaskView getLastGridTaskView() {
|
||||
protected TaskView getLastGridTaskView() {
|
||||
return getLastGridTaskView(mUtils.getTopRowIdArray(), mUtils.getBottomRowIdArray());
|
||||
}
|
||||
|
||||
@@ -1532,7 +1532,7 @@ public abstract class RecentsView<
|
||||
return getTaskViewFromTaskViewId(lastTaskViewId);
|
||||
}
|
||||
|
||||
private int getSnapToLastTaskScrollDiff() {
|
||||
protected int getSnapToLastTaskScrollDiff() {
|
||||
// Snap to a position where ClearAll is just invisible.
|
||||
int screenStart = getPagedOrientationHandler().getPrimaryScroll(this);
|
||||
int clearAllScroll = getScrollForPage(indexOfChild(mClearAllButton));
|
||||
@@ -1541,7 +1541,7 @@ public abstract class RecentsView<
|
||||
return screenStart - lastTaskScroll;
|
||||
}
|
||||
|
||||
private int getLastTaskScroll(int clearAllScroll, int clearAllWidth) {
|
||||
protected int getLastTaskScroll(int clearAllScroll, int clearAllWidth) {
|
||||
int distance = clearAllWidth + getClearAllExtraPageSpacing();
|
||||
return clearAllScroll + (mIsRtl ? distance : -distance);
|
||||
}
|
||||
@@ -2871,6 +2871,10 @@ public abstract class RecentsView<
|
||||
return tv == null ? -1 : indexOfChild(tv);
|
||||
}
|
||||
|
||||
protected int getClearAllShortTotalWidthTranslation() {
|
||||
return mClearAllShortTotalWidthTranslation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the view if anything in recents changed.
|
||||
*/
|
||||
@@ -3716,11 +3720,14 @@ public abstract class RecentsView<
|
||||
* selection state from app icon
|
||||
* @param isExpressiveDismiss runs expressive animations controlled via
|
||||
* {@link RecentsDismissUtils}
|
||||
* @param gridEndData used to compute any gap left between the end of the
|
||||
* grid and the clear all button.
|
||||
*/
|
||||
public void createTaskDismissAnimation(PendingAnimation anim,
|
||||
@Nullable TaskView dismissedTaskView,
|
||||
boolean animateTaskView, boolean shouldRemoveTask, long duration,
|
||||
boolean dismissingForSplitSelection, boolean isExpressiveDismiss) {
|
||||
boolean dismissingForSplitSelection, boolean isExpressiveDismiss,
|
||||
@Nullable RecentsDismissUtils.GridEndData gridEndData) {
|
||||
if (mPendingAnimation != null) {
|
||||
mPendingAnimation.createPlaybackController().dispatchOnCancel().dispatchOnEnd();
|
||||
}
|
||||
@@ -3739,7 +3746,7 @@ public abstract class RecentsView<
|
||||
// Grid specific properties.
|
||||
boolean isFocusedTaskDismissed = false;
|
||||
boolean isStagingFocusedTask = false;
|
||||
boolean isSlidingTasks = false;
|
||||
boolean isSlidingTasks;
|
||||
TaskView nextFocusedTaskView = null;
|
||||
boolean nextFocusedTaskFromTop = false;
|
||||
float dismissedTaskWidth = 0;
|
||||
@@ -3799,156 +3806,69 @@ public abstract class RecentsView<
|
||||
float dismissTranslationInterpolationEnd = 1;
|
||||
boolean closeGapBetweenClearAll = false;
|
||||
boolean isClearAllHidden = isClearAllHidden();
|
||||
boolean snapToLastTask = false;
|
||||
boolean isLeftRightSplit =
|
||||
mContainer.getDeviceProfile().isLeftRightSplit && isSplitSelectionActive();
|
||||
TaskView lastGridTaskView = showAsGrid ? getLastGridTaskView() : null;
|
||||
int currentPageScroll = getScrollForPage(mCurrentPage);
|
||||
int lastGridTaskScroll = getScrollForPage(indexOfChild(lastGridTaskView));
|
||||
boolean currentPageSnapsToEndOfGrid = currentPageScroll == lastGridTaskScroll;
|
||||
|
||||
int topGridRowSize = mTopRowIdSet.size();
|
||||
int numLargeTiles = mUtils.getLargeTileCount();
|
||||
int bottomGridRowSize = taskCount - mTopRowIdSet.size() - numLargeTiles;
|
||||
boolean topRowLonger = topGridRowSize > bottomGridRowSize;
|
||||
boolean bottomRowLonger = bottomGridRowSize > topGridRowSize;
|
||||
boolean dismissedTaskFromTop = mTopRowIdSet.contains(dismissedTaskViewId);
|
||||
boolean dismissedTaskFromBottom = !dismissedTaskFromTop && !isFocusedTaskDismissed;
|
||||
if (dismissedTaskFromTop || (isFocusedTaskDismissed && nextFocusedTaskFromTop)) {
|
||||
topGridRowSize--;
|
||||
if (gridEndData == null) {
|
||||
gridEndData = mDismissUtils.getGridEndData(dismissedTaskView, isExpressiveDismiss,
|
||||
isFocusedTaskDismissed, nextFocusedTaskView, isStagingFocusedTask,
|
||||
nextFocusedTaskFromTop, nextFocusedTaskWidth);
|
||||
}
|
||||
if (dismissedTaskFromBottom || (isFocusedTaskDismissed && !nextFocusedTaskFromTop)) {
|
||||
bottomGridRowSize--;
|
||||
float longGridRowWidthDiff = gridEndData.getGridEndOffset();
|
||||
boolean snapToLastTask = gridEndData.getSnapToLastTask();
|
||||
float newClearAllShortTotalWidthTranslation =
|
||||
gridEndData.getNewClearAllShortTotalWidthTranslation();
|
||||
boolean currentPageSnapsToEndOfGrid = gridEndData.getCurrentPageSnapsToEndOfGrid();
|
||||
if (longGridRowWidthDiff != 0f) {
|
||||
closeGapBetweenClearAll = true;
|
||||
}
|
||||
int longRowWidth = Math.max(topGridRowSize, bottomGridRowSize)
|
||||
* (mLastComputedGridTaskSize.width() + mPageSpacing);
|
||||
if (!enableGridOnlyOverview() && !isStagingFocusedTask) {
|
||||
longRowWidth += mLastComputedTaskSize.width() + mPageSpacing;
|
||||
}
|
||||
// Compensate the removed gap if we don't already have shortTotalCompensation,
|
||||
// and adjust accordingly to the new shortTotalCompensation after dismiss.
|
||||
int newClearAllShortTotalWidthTranslation = 0;
|
||||
if (mClearAllShortTotalWidthTranslation == 0) {
|
||||
// If first task is not in the expected position (mLastComputedTaskSize) and being too
|
||||
// close to ClearAllButton, then apply extra translation to ClearAllButton.
|
||||
int firstTaskStart = mLastComputedGridSize.left + longRowWidth;
|
||||
int expectedFirstTaskStart = mLastComputedTaskSize.right;
|
||||
if (firstTaskStart < expectedFirstTaskStart) {
|
||||
newClearAllShortTotalWidthTranslation = expectedFirstTaskStart - firstTaskStart;
|
||||
}
|
||||
}
|
||||
if (lastGridTaskView != null && (
|
||||
(!isExpressiveDismiss && lastGridTaskView.isVisibleToUser()) || (isExpressiveDismiss
|
||||
&& (isTaskViewVisible(lastGridTaskView)
|
||||
|| lastGridTaskView == dismissedTaskView)))) {
|
||||
// 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;
|
||||
|
||||
float gapWidth = 0;
|
||||
if ((topRowLonger && dismissedTaskFromTop)
|
||||
|| (bottomRowLonger && dismissedTaskFromBottom)) {
|
||||
gapWidth = dismissedTaskWidth;
|
||||
} else if (nextFocusedTaskView != null
|
||||
&& ((topRowLonger && nextFocusedTaskFromTop)
|
||||
|| (bottomRowLonger && !nextFocusedTaskFromTop))) {
|
||||
gapWidth = nextFocusedTaskWidth;
|
||||
}
|
||||
if (gapWidth > 0) {
|
||||
if (mClearAllShortTotalWidthTranslation == 0) {
|
||||
float gapCompensation = gapWidth - newClearAllShortTotalWidthTranslation;
|
||||
longGridRowWidthDiff += mIsRtl ? -gapCompensation : gapCompensation;
|
||||
}
|
||||
if (isClearAllHidden) {
|
||||
// If ClearAllButton isn't fully shown, snap to the last task.
|
||||
snapToLastTask = true;
|
||||
}
|
||||
}
|
||||
if (isLeftRightSplit && !isStagingFocusedTask) {
|
||||
// LastTask's scroll is the minimum scroll in split select, if current scroll is
|
||||
// beyond that, we'll need to snap to last task instead.
|
||||
TaskView lastTask = getLastGridTaskView();
|
||||
if (lastTask != null) {
|
||||
int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
|
||||
int lastTaskScroll = getScrollForPage(indexOfChild(lastTask));
|
||||
if ((mIsRtl && primaryScroll < lastTaskScroll)
|
||||
|| (!mIsRtl && primaryScroll > lastTaskScroll)) {
|
||||
snapToLastTask = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (snapToLastTask) {
|
||||
longGridRowWidthDiff += getSnapToLastTaskScrollDiff();
|
||||
} else if (isLeftRightSplit && currentPageSnapsToEndOfGrid) {
|
||||
// Use last task as reference point for scroll diff and snapping calculation as it's
|
||||
// the only invariant point in landscape split screen.
|
||||
snapToLastTask = true;
|
||||
}
|
||||
if (mUtils.getGridTaskCount() == 1 && dismissedTaskView.isGridTask()) {
|
||||
TaskView lastLargeTile = mUtils.getLastLargeTaskView();
|
||||
if (lastLargeTile != null) {
|
||||
// Calculate the distance to put last large tile back to middle of the screen.
|
||||
int primaryScroll = getPagedOrientationHandler().getPrimaryScroll(this);
|
||||
int lastLargeTileScroll = getScrollForPage(indexOfChild(lastLargeTile));
|
||||
longGridRowWidthDiff = primaryScroll - lastLargeTileScroll;
|
||||
|
||||
if (!isClearAllHidden) {
|
||||
// If ClearAllButton is visible, reduce the distance by scroll difference
|
||||
// between ClearAllButton and the last task.
|
||||
longGridRowWidthDiff += getLastTaskScroll(/*clearAllScroll=*/0,
|
||||
getPagedOrientationHandler().getPrimarySize(mClearAllButton));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
// 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.
|
||||
if (!isExpressiveDismiss && longGridRowWidthDiff != 0) {
|
||||
// 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. 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 (TaskView taskView : getTaskViews()) {
|
||||
anim.setFloat(taskView, TaskView.GRID_END_TRANSLATION_X,
|
||||
longGridRowWidthDiff,
|
||||
clampToProgress(LINEAR, dismissTranslationInterpolationEnd, 1));
|
||||
dismissTranslationInterpolationEnd = Utilities.boundToRange(
|
||||
END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
|
||||
+ (taskCount - 1) * halfAdditionalDismissTranslationOffset,
|
||||
dismissTranslationInterpolationEnd
|
||||
- halfAdditionalDismissTranslationOffset,
|
||||
END_DISMISS_TRANSLATION_INTERPOLATION_OFFSET, 1);
|
||||
for (TaskView taskView : getTaskViews()) {
|
||||
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 (mEnableDrawingLiveTile && taskView.isRunningTask()) {
|
||||
anim.addOnFrameCallback(() -> {
|
||||
runActionOnRemoteHandles(
|
||||
remoteTargetHandle ->
|
||||
remoteTargetHandle.getTaskViewSimulator()
|
||||
.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);
|
||||
}
|
||||
if (mEnableDrawingLiveTile && taskView.isRunningTask()) {
|
||||
anim.addOnFrameCallback(() -> {
|
||||
runActionOnRemoteHandles(remoteTargetHandle ->
|
||||
remoteTargetHandle.getTaskViewSimulator()
|
||||
.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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
SplitAnimationTimings splitTimings =
|
||||
@@ -4636,17 +4556,17 @@ public abstract class RecentsView<
|
||||
PendingAnimation pa = new PendingAnimation(DISMISS_TASK_DURATION);
|
||||
createTaskDismissAnimation(pa, taskView, animateTaskView, removeTask,
|
||||
DISMISS_TASK_DURATION, false /* dismissingForSplitSelection*/,
|
||||
false /* isExpressiveDismiss */);
|
||||
false /* isExpressiveDismiss */, null /* gridEndData */);
|
||||
runDismissAnimation(pa);
|
||||
}
|
||||
}
|
||||
|
||||
protected void expressiveDismissTaskView(TaskView taskView, Function0<Unit> onEndRunnable,
|
||||
int dismissDuration) {
|
||||
int dismissDuration, @Nullable RecentsDismissUtils.GridEndData gridEndData) {
|
||||
PendingAnimation pa = new PendingAnimation(dismissDuration);
|
||||
createTaskDismissAnimation(pa, taskView, false /* animateTaskView */, true /* removeTask */,
|
||||
dismissDuration, false /* dismissingForSplitSelection*/,
|
||||
true /* isExpressiveDismiss */);
|
||||
true /* isExpressiveDismiss */, gridEndData);
|
||||
pa.addEndListener((success) -> onEndRunnable.invoke());
|
||||
runDismissAnimation(pa);
|
||||
}
|
||||
@@ -5381,7 +5301,8 @@ public abstract class RecentsView<
|
||||
}
|
||||
// Splitting from Overview for fullscreen task
|
||||
createTaskDismissAnimation(builder, mSplitHiddenTaskView, true, false, duration,
|
||||
true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
|
||||
true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */,
|
||||
null /* gridEndData */);
|
||||
} else {
|
||||
// Splitting from Home
|
||||
TaskView currentPageTaskView = getTaskViewAt(mCurrentPage);
|
||||
@@ -5389,7 +5310,8 @@ public abstract class RecentsView<
|
||||
// display correct animation in split mode
|
||||
if (currentPageTaskView instanceof DesktopTaskView) {
|
||||
createTaskDismissAnimation(builder, null, true, false, duration,
|
||||
true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */);
|
||||
true /* dismissingForSplitSelection*/, false /* isExpressiveDismiss */,
|
||||
null /* gridEndData */);
|
||||
} else {
|
||||
createInitialSplitSelectAnimation(builder);
|
||||
}
|
||||
|
||||
@@ -115,6 +115,8 @@
|
||||
<item name="expressive_dismiss_task_trans_y_stiffness" type="dimen" format="float">900</item>
|
||||
<item name="expressive_dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.8</item>
|
||||
<item name="expressive_dismiss_task_trans_x_stiffness" type="dimen" format="float">900</item>
|
||||
<item name="expressive_dismiss_effects_damping_ratio" type="dimen" format="float">1</item>
|
||||
<item name="expressive_dismiss_effects_stiffness" type="dimen" format="float">1600</item>
|
||||
|
||||
<!-- Taskbar -->
|
||||
<!-- This is a float because it is converted to dp later in DeviceProfile -->
|
||||
|
||||
Reference in New Issue
Block a user