Merge "Animate task translation on dismissal." into sc-dev

This commit is contained in:
Pat Manning
2021-05-18 09:32:19 +00:00
committed by Android (Google) Code Review
4 changed files with 94 additions and 57 deletions

View File

@@ -39,6 +39,11 @@
<dimen name="overview_grid_focus_vertical_margin">90dp</dimen>
<dimen name="split_placeholder_size">110dp</dimen>
<!-- These speeds are in dp/s -->
<dimen name="max_task_dismiss_drag_velocity">2.25dp</dimen>
<dimen name="default_task_dismiss_drag_velocity">1.75dp</dimen>
<dimen name="default_task_dismiss_drag_velocity_grid">0.75dp</dimen>
<dimen name="recents_page_spacing">16dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>

View File

@@ -28,6 +28,7 @@ import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
@@ -50,6 +51,10 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
extends AnimatorListenerAdapter implements TouchController,
SingleAxisSwipeDetector.Listener {
private static final float ANIMATION_PROGRESS_FRACTION_MIDPOINT = 0.5f;
private static final long MIN_TASK_DISMISS_ANIMATION_DURATION = 300;
private static final long MAX_TASK_DISMISS_ANIMATION_DURATION = 600;
protected final T mActivity;
private final SingleAxisSwipeDetector mDetector;
private final RecentsView mRecentsView;
@@ -277,14 +282,32 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
} else {
mFlingBlockCheck.onEvent();
}
mCurrentAnimation.setPlayFraction(Utilities.boundToRange(
totalDisplacement * mProgressMultiplier, 0, 1));
// Once halfway through task dismissal interpolation, switch from reversible dragging-task
// animation to playing the remaining task translation animations
if (mCurrentAnimation.getProgressFraction() < ANIMATION_PROGRESS_FRACTION_MIDPOINT) {
// Halve the value as we are animating the drag across the full length for only the
// first half of the progress
mCurrentAnimation.setPlayFraction(
Utilities.boundToRange(totalDisplacement * mProgressMultiplier / 2, 0, 1));
} else {
float dragVelocity = -mTaskBeingDragged.getResources().getDimension(
mRecentsView.showAsGrid() ? R.dimen.default_task_dismiss_drag_velocity_grid
: R.dimen.default_task_dismiss_drag_velocity);
onDragEnd(dragVelocity);
return true;
}
return true;
}
@Override
public void onDragEnd(float velocity) {
// Limit velocity, as very large scalar values make animations play too quickly
float maxTaskDismissDragVelocity = mTaskBeingDragged.getResources().getDimension(
R.dimen.max_task_dismiss_drag_velocity);
velocity = Utilities.boundToRange(velocity, -maxTaskDismissDragVelocity,
maxTaskDismissDragVelocity);
boolean fling = mDetector.isFling(velocity);
final boolean goingToEnd;
boolean blockedFling = fling && mFlingBlockCheck.isBlocked();
@@ -305,6 +328,11 @@ public abstract class TaskViewTouchController<T extends BaseDraggingActivity>
if (blockedFling && !goingToEnd) {
animationDuration *= LauncherAnimUtils.blockedFlingDurationFactor(velocity);
}
// Due to very high or low velocity dismissals, animation durations can be inconsistently
// long or short. Bound the duration for animation of task translations for a more
// standardized feel.
animationDuration = Utilities.boundToRange(animationDuration,
MIN_TASK_DISMISS_ANIMATION_DURATION, MAX_TASK_DISMISS_ANIMATION_DURATION);
mCurrentAnimation.setEndAction(this::clearState);
mCurrentAnimation.startWithVelocity(mActivity, goingToEnd,

View File

@@ -32,11 +32,12 @@ import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_5;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
@@ -336,6 +337,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// OverScroll constants
private static final int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
private static final int DISMISS_TASK_DURATION = 300;
private static final int ADDITION_TASK_DURATION = 200;
private static final float INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.55f;
private static final float ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET = 0.05f;
protected final RecentsOrientedState mOrientationState;
protected final BaseActivityInterface<STATE_TYPE, ACTIVITY_TYPE> mSizeStrategy;
protected RecentsAnimationController mRecentsAnimationController;
@@ -358,10 +364,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private final List<OnScrollChangedListener> mScrollListeners = new ArrayList<>();
private float mFullscreenScale;
private static final int DISMISS_TASK_DURATION = 300;
private static final int DISMISS_TASK_TRANSLATION_DURATION = 200;
private static final int ADDITIONAL_DISMISS_TASK_TRANSLATION_DURATION = 75;
private static final int ADDITION_TASK_DURATION = 200;
// 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;
@@ -1886,21 +1888,18 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
* This method is used when no task dismissal has occurred.
*/
private void updateGridProperties() {
updateGridProperties(null, -1);
updateGridProperties(false);
}
/**
* Updates TaskView and ClearAllButton scaling and translation required to turn into grid
* layout.
* This method only calculates the potential position and depends on {@link #setGridProgress} to
* apply the actual scaling and translation. This adds task translation animations in the case
* of task dismissals: e.g. when dismissedTask is not null.
* apply the actual scaling and translation.
*
* @param dismissedTask the TaskView dismissed, possibly null
* @param dismissedIndex the index at which the dismissedTask was prior to dismissal, if no
* dismissal occurred, this is unused
* @param isTaskDismissal indicates if update was called due to task dismissal
*/
private void updateGridProperties(TaskView dismissedTask, int dismissedIndex) {
private void updateGridProperties(boolean isTaskDismissal) {
int taskCount = getTaskViewCount();
if (taskCount == 0) {
return;
@@ -1940,10 +1939,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
int snappedPage = getNextPage();
TaskView snappedTaskView = getTaskViewAtByAbsoluteIndex(snappedPage);
boolean isTaskDismissal = dismissedTask != null;
float dismissedTaskWidth =
isTaskDismissal ? dismissedTask.getLayoutParams().width + mPageSpacing : 0;
if (!isTaskDismissal) {
mTopRowIdSet.clear();
}
@@ -2039,34 +2034,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
snappedTaskGridTranslationX = gridTranslations[snappedPage - mTaskViewStartIndex];
}
// Animate task dismissTranslationX for tasks with index >= dismissed index and in the
// same row as the dismissed index, or if the dismissed task was the focused task. Offset
// successive task dismissal durations for a staggered effect.
ArrayList<Animator> gridTranslationAnimators = new ArrayList<>();
boolean isFocusedTaskDismissed =
isTaskDismissal && dismissedTask.getTask().key.id == mFocusedTaskId;
for (int i = 0; i < taskCount; i++) {
TaskView taskView = getTaskViewAt(i);
if (isFocusedTaskDismissed || (i >= dismissedIndex && isSameGridRow(dismissedTask,
taskView))) {
Animator taskDismissAnimator = ObjectAnimator.ofFloat(taskView,
taskView.getPrimaryDismissTranslationProperty(),
mIsRtl ? -dismissedTaskWidth : dismissedTaskWidth, 0f);
int additionalTranslationDuration =
i >= dismissedIndex ? (ADDITIONAL_DISMISS_TASK_TRANSLATION_DURATION * (
(i - dismissedIndex) / 2)) : 0;
taskDismissAnimator.setDuration(
DISMISS_TASK_TRANSLATION_DURATION + additionalTranslationDuration);
gridTranslationAnimators.add(taskDismissAnimator);
}
taskView.setGridTranslationX(gridTranslations[i] - snappedTaskGridTranslationX);
taskView.getPrimaryNonFullscreenTranslationProperty().set(taskView,
snappedTaskFullscreenScrollAdjustment);
taskView.getSecondaryNonFullscreenTranslationProperty().set(taskView, 0f);
}
AnimatorSet gridTranslationAnimatorSet = new AnimatorSet();
gridTranslationAnimatorSet.playTogether(gridTranslationAnimators);
gridTranslationAnimatorSet.start();
// Use the accumulated translation of the row containing the last task.
float clearAllAccumulatedTranslation = topSet.contains(taskCount - 1)
@@ -2210,7 +2184,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
PendingAnimation anim) {
// Use setFloat instead of setViewAlpha as we want to keep the view visible even when it's
// alpha is set to 0 so that it can be recycled in the view pool properly
anim.setFloat(taskView, VIEW_ALPHA, 0, ACCEL_2);
anim.setFloat(taskView, VIEW_ALPHA, 0, clampToProgress(ACCEL, 0, 0.5f));
SplitSelectStateController splitController = mSplitPlaceholderView.getSplitController();
ResourceProvider rp = DynamicResource.provider(mActivity);
@@ -2246,8 +2220,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
throw new IllegalStateException("Invalid split task translation: " + dir);
}
}
// Double translation distance so dismissal drag is the full height, as we only animate
// the drag for the first half of the progress.
anim.add(ObjectAnimator.ofFloat(taskView, dismissingTaskViewTranslate,
positiveNegativeFactor * translateDistance).setDuration(duration), LINEAR, sp);
positiveNegativeFactor * translateDistance * 2).setDuration(duration), LINEAR, sp);
if (LIVE_TILE.get() && taskView.isRunningTask()) {
anim.addOnFrameCallback(() -> {
@@ -2283,6 +2259,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
int draggedIndex = indexOfChild(taskView);
boolean isFocusedTaskDismissed = taskView.getTask().key.id == mFocusedTaskId;
if (isFocusedTaskDismissed && showAsGrid()) {
anim.setFloat(mActionsView, VIEW_ALPHA, 0, clampToProgress(ACCEL_0_5, 0, 0.5f));
}
float dismissedTaskWidth = taskView.getLayoutParams().width + mPageSpacing;
boolean needsCurveUpdates = false;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
@@ -2291,7 +2272,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
addDismissedTaskAnimations(taskView, duration, anim);
}
} else if (!showAsGrid()) {
// For grid layout, don't animate other tasks when dismissing in grid for now.
// Compute scroll offsets from task dismissal for animation.
// If we just take newScroll - oldScroll, everything to the right of dragged task
// translates to the left. We need to offset this in some cases:
// - In RTL, add page offset to all pages, since we want pages to move to the right
@@ -2318,15 +2299,31 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
? ((TaskView) child).getPrimaryDismissTranslationProperty()
: mOrientationHandler.getPrimaryViewTranslate();
ResourceProvider rp = DynamicResource.provider(mActivity);
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
.setDampingRatio(
rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio))
.setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness));
anim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
.setDuration(duration), ACCEL, sp);
float additionalDismissDuration =
ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
i - draggedIndex);
anim.setFloat(child, translationProperty, scrollDiff, clampToProgress(LINEAR,
Utilities.boundToRange(INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ additionalDismissDuration, 0f, 1f), 1));
needsCurveUpdates = true;
}
} else if (child instanceof TaskView) {
// Animate task with index >= dismissed index and in the same row as the
// dismissed index, or if the dismissed task was the focused task. Offset
// successive task dismissal durations for a staggered effect.
if (isFocusedTaskDismissed || (i >= draggedIndex && isSameGridRow((TaskView) child,
taskView))) {
FloatProperty translationProperty =
((TaskView) child).getPrimaryDismissTranslationProperty();
float additionalDismissDuration =
ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET * Math.abs(
i - draggedIndex);
anim.setFloat(child, translationProperty,
!mIsRtl ? -dismissedTaskWidth : dismissedTaskWidth,
clampToProgress(LINEAR, Utilities.boundToRange(
INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET
+ additionalDismissDuration, 0f, 1f), 1));
}
}
}
@@ -2364,6 +2361,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
}
}
// Reset task translations as they may have updated via animations in
// createTaskDismissAnimation
resetTaskVisuals();
int pageToSnapTo = mCurrentPage;
// Snap to start if focused task was dismissed, as after quick switch it could
// be at any page but the focused task always displays at the start.
@@ -2381,7 +2382,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
} else {
snapToPageImmediately(pageToSnapTo);
// Grid got messed up, reapply.
updateGridProperties(taskView, draggedIndex - mTaskViewStartIndex);
updateGridProperties(true);
if (showAsGrid() && getFocusedTaskView() == null
&& mActionsView.getVisibilityAlpha().getValue() == 1) {
animateActionsViewOut();
@@ -2391,9 +2392,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
// immediately available.
onLayout(false /* changed */, getLeft(), getTop(), getRight(), getBottom());
}
if (!showAsGrid()) {
resetTaskVisuals();
}
onDismissAnimationEnds();
mPendingAnimation = null;
}
@@ -3721,7 +3719,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
return mColorTint;
}
private boolean showAsGrid() {
/** Returns {@code true} if the overview tasks are displayed as a grid. */
public boolean showAsGrid() {
return mOverviewGridEnabled || (mCurrentGestureEndTarget != null
&& mSizeStrategy.stateFromGestureEndTarget(
mCurrentGestureEndTarget).displayOverviewTasksAsGrid(mActivity.getDeviceProfile()));

View File

@@ -35,6 +35,7 @@ public class Interpolators {
public static final Interpolator LINEAR = new LinearInterpolator();
public static final Interpolator ACCEL = new AccelerateInterpolator();
public static final Interpolator ACCEL_0_5 = new AccelerateInterpolator(0.5f);
public static final Interpolator ACCEL_0_75 = new AccelerateInterpolator(0.75f);
public static final Interpolator ACCEL_1_5 = new AccelerateInterpolator(1.5f);
public static final Interpolator ACCEL_2 = new AccelerateInterpolator(2);
@@ -149,11 +150,15 @@ public class Interpolators {
*/
public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
float upperBound) {
if (upperBound <= lowerBound) {
throw new IllegalArgumentException(String.format(
"lowerBound (%f) must be less than upperBound (%f)", lowerBound, upperBound));
if (upperBound < lowerBound) {
throw new IllegalArgumentException(
String.format("upperBound (%f) must be greater than lowerBound (%f)",
upperBound, lowerBound));
}
return t -> {
if (t == lowerBound && t == upperBound) {
return t == 0f ? 0 : 1;
}
if (t < lowerBound) {
return 0;
}