From a50bb39048f21bd0a3baff6836fde81853a6c32b Mon Sep 17 00:00:00 2001 From: Jeremy Sim Date: Thu, 28 Jul 2022 17:09:41 -0700 Subject: [PATCH] Update animations for TM-QPR: Overview > OverviewSplitSelect transition The animation from Overview to OverviewSplitSelect is now updated to the latest spec. See bug for details. - New timings - Task thumbnails slide in with a new spring animation - Icons fade out and fade in appropriately - SplitInstructionsView has a new compound "unfold" animation Bug: 236760307 Test: Manual on tablet and phone. Looks good on both true and fake orientations. Change-Id: If98d4464aa51c876d79bee9672279992a2cf9026 --- .../QuickstepAtomicAnimationFactory.java | 3 + .../states/SplitScreenSelectState.java | 9 ++ .../quickstep/views/FloatingTaskView.java | 26 ++++-- .../android/quickstep/views/RecentsView.java | 92 ++++++++++++++++--- .../views/SplitInstructionsView.java | 35 ++++++- .../quickstep/views/SplitPlaceholderView.java | 13 +++ .../com/android/quickstep/views/TaskView.java | 13 +++ .../android/launcher3/anim/Interpolators.java | 2 + .../touch/LandscapePagedViewHandler.java | 6 +- .../touch/PortraitPagedViewHandler.java | 2 +- .../touch/SeascapePagedViewHandler.java | 7 +- 11 files changed, 175 insertions(+), 33 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java index 872e64a510..cb1da383d5 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java @@ -188,6 +188,9 @@ public class QuickstepAtomicAnimationFactory extends AllAppsSwipeController.applyAllAppsToNormalConfig(mActivity, config); } else if (fromState == NORMAL && toState == ALL_APPS) { AllAppsSwipeController.applyNormalToAllAppsAnimConfig(mActivity, config); + } else if (fromState == OVERVIEW && toState == OVERVIEW_SPLIT_SELECT) { + config.setInterpolator(ANIM_OVERVIEW_ACTIONS_FADE, + clampToProgress(LINEAR, 0, 0.167f)); } } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java index e79d56b631..cb08ac8b5e 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/states/SplitScreenSelectState.java @@ -16,6 +16,8 @@ package com.android.launcher3.uioverrides.states; +import android.content.Context; + import com.android.launcher3.Launcher; import com.android.quickstep.views.RecentsView; @@ -24,6 +26,8 @@ import com.android.quickstep.views.RecentsView; * pinned and user is selecting the second one */ public class SplitScreenSelectState extends OverviewState { + private static final int OVERVIEW_SPLIT_SELECT_SLIDE_IN_DURATION = 500; + public SplitScreenSelectState(int id) { super(id); } @@ -38,4 +42,9 @@ public class SplitScreenSelectState extends OverviewState { RecentsView recentsView = launcher.getOverviewPanel(); return recentsView.getSplitSelectTranslation(); } + + @Override + public int getTransitionDuration(Context context, boolean isToState) { + return OVERVIEW_SPLIT_SELECT_SLIDE_IN_DURATION; + } } diff --git a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java index 7a66ea00e1..d93f015e7f 100644 --- a/quickstep/src/com/android/quickstep/views/FloatingTaskView.java +++ b/quickstep/src/com/android/quickstep/views/FloatingTaskView.java @@ -2,8 +2,9 @@ package com.android.quickstep.views; import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; import static com.android.launcher3.anim.Interpolators.ACCEL; -import static com.android.launcher3.anim.Interpolators.DEACCEL_3; +import static com.android.launcher3.anim.Interpolators.FASTER_OUT_SLOWER_IN; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.launcher3.anim.Interpolators.clampToProgress; import android.animation.ValueAnimator; import android.content.Context; @@ -200,10 +201,16 @@ public class FloatingTaskView extends FrameLayout { RectF floatingTaskViewBounds = new RectF(); if (fadeWithThumbnail) { - animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT, - 0, 1, ACCEL); + // FloatingTaskThumbnailView: thumbnail fades out to transparent animation.addFloat(mThumbnailView, LauncherAnimUtils.VIEW_ALPHA, - 1, 0, DEACCEL_3); + 1, 0, clampToProgress(LINEAR, 0, 0.267f)); + + // SplitPlaceholderView: gray background fades in at the same time, then new icon fades + // in + animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ALPHA_FLOAT, + 0, 1, clampToProgress(LINEAR, 0, 0.267f)); + animation.addFloat(mSplitPlaceholderView, SplitPlaceholderView.ICON_ALPHA, + 0, 1, clampToProgress(LINEAR, 0.333f, 0.5f)); } else if (isStagedTask) { // Fade in the placeholder view when split is initiated from homescreen / all apps // icons. @@ -214,12 +221,15 @@ public class FloatingTaskView extends FrameLayout { } MultiValueUpdateListener listener = new MultiValueUpdateListener() { - final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, LINEAR); - final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, LINEAR); + // SplitPlaceholderView: rectangle translates and stretches to new position + final FloatProp mDx = new FloatProp(0, prop.dX, 0, animDuration, + clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f)); + final FloatProp mDy = new FloatProp(0, prop.dY, 0, animDuration, + clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f)); final FloatProp mTaskViewScaleX = new FloatProp(1f, prop.finalTaskViewScaleX, 0, - animDuration, LINEAR); + animDuration, clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f)); final FloatProp mTaskViewScaleY = new FloatProp(1f, prop.finalTaskViewScaleY, 0, - animDuration, LINEAR); + animDuration, clampToProgress(FASTER_OUT_SLOWER_IN, 0, 0.833f)); @Override public void onUpdate(float percent, boolean initOnly) { // Calculate the icon position. diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 64068adcc6..a153f2676e 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -35,6 +35,7 @@ import static com.android.launcher3.anim.Interpolators.ACCEL; import static com.android.launcher3.anim.Interpolators.ACCEL_0_75; import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; +import static com.android.launcher3.anim.Interpolators.EMPHASIZED_DECELERATE; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.FINAL_FRAME; import static com.android.launcher3.anim.Interpolators.LINEAR; @@ -113,6 +114,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.core.graphics.ColorUtils; +import androidx.dynamicanimation.animation.SpringForce; import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener; @@ -125,6 +127,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.PendingAnimation; +import com.android.launcher3.anim.SpringAnimationBuilder; import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.compat.AccessibilityManagerCompat; import com.android.launcher3.config.FeatureFlags; @@ -442,6 +445,9 @@ public abstract class RecentsView= dismissed index and in the same row as the // dismissed index or next focused index. Offset successive task dismissal // durations for a staggered effect. - float animationStartProgress = Utilities.boundToRange( - INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET - + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET - * ++distanceFromDismissedTask, 0f, - dismissTranslationInterpolationEnd); + distanceFromDismissedTask++; + // If user is initiating splitscreen from the focused (large) task, we use a + // spring-based animation and timings. For other, smaller, repositions, we currently + // fall back on a less complicated linear animation and timings. + float animationStartProgress = isFocusedTaskDismissed && nextFocusedTaskView == null + ? Utilities.boundToRange( + INITIAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET + + ADDITIONAL_SPRING_DISMISS_TRANSLATION_INTERPOLATION_OFFSET + * (int) Math.ceil(distanceFromDismissedTask / 2f), 0f, + dismissTranslationInterpolationEnd) + : Utilities.boundToRange( + INITIAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET + + ADDITIONAL_DISMISS_TRANSLATION_INTERPOLATION_OFFSET + * distanceFromDismissedTask, 0f, + dismissTranslationInterpolationEnd); + if (taskView == nextFocusedTaskView) { // Enlarge the task to be focused next, and translate into focus position. float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width(); @@ -3163,12 +3199,36 @@ public abstract class RecentsView() { + + Consumer endConsumer = new Consumer() { @Override public void accept(Boolean success) { if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile @@ -3399,7 +3460,9 @@ public abstract class RecentsView ALPHA_FLOAT = - new FloatProperty("SplitInstructionsAlpha") { + public static final FloatProperty CONTAINER_ALPHA = + new FloatProperty("SplitInstructionsContainerAlpha") { @Override public void setValue(SplitInstructionsView splitInstructionsView, float v) { splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE); @@ -55,6 +57,32 @@ public class SplitInstructionsView extends FrameLayout { } }; + public static final FloatProperty UNFOLD = + new FloatProperty("SplitInstructionsUnfold") { + @Override + public void setValue(SplitInstructionsView splitInstructionsView, float v) { + splitInstructionsView.setScaleY(v); + } + + @Override + public Float get(SplitInstructionsView splitInstructionsView) { + return splitInstructionsView.getScaleY(); + } + }; + + public static final FloatProperty TEXT_ALPHA = + new FloatProperty("SplitInstructionsTextAlpha") { + @Override + public void setValue(SplitInstructionsView splitInstructionsView, float v) { + splitInstructionsView.mTextView.setAlpha(v); + } + + @Override + public Float get(SplitInstructionsView splitInstructionsView) { + return splitInstructionsView.mTextView.getAlpha(); + } + }; + public SplitInstructionsView(Context context) { this(context, null); } @@ -77,6 +105,9 @@ public class SplitInstructionsView extends FrameLayout { false ); + splitInstructionsView.mTextView = splitInstructionsView.findViewById( + R.id.split_instructions_text); + dragLayer.addView(splitInstructionsView); return splitInstructionsView; } diff --git a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java index 28080d477f..ae6aae1e2d 100644 --- a/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java +++ b/quickstep/src/com/android/quickstep/views/SplitPlaceholderView.java @@ -47,6 +47,19 @@ public class SplitPlaceholderView extends FrameLayout { } }; + public static final FloatProperty ICON_ALPHA = + new FloatProperty("SplitViewIconAlpha") { + @Override + public void setValue(SplitPlaceholderView splitPlaceholderView, float v) { + splitPlaceholderView.mIconView.setAlpha(v); + } + + @Override + public Float get(SplitPlaceholderView splitPlaceholderView) { + return splitPlaceholderView.mIconView.getAlpha(); + } + }; + @Nullable private IconView mIconView; diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index b089155f5b..d2c298816f 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -323,6 +323,19 @@ public class TaskView extends FrameLayout implements Reusable { } }; + public static final FloatProperty ICON_ALPHA = + new FloatProperty("iconAlpha") { + @Override + public void setValue(TaskView taskView, float v) { + taskView.mIconView.setAlpha(v); + } + + @Override + public Float get(TaskView taskView) { + return taskView.mIconView.getAlpha(); + } + }; + @Nullable protected Task mTask; protected TaskThumbnailView mSnapshotView; diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java index 0a77aa7bcf..464b3eda5b 100644 --- a/src/com/android/launcher3/anim/Interpolators.java +++ b/src/com/android/launcher3/anim/Interpolators.java @@ -50,6 +50,8 @@ public class Interpolators { public static final Interpolator ACCEL_DEACCEL = new AccelerateDecelerateInterpolator(); public static final Interpolator FAST_OUT_SLOW_IN = new PathInterpolator(0.4f, 0f, 0.2f, 1f); + public static final Interpolator FASTER_OUT_SLOWER_IN = + new PathInterpolator(0.3f, 0f, 0.1f, 1f); public static final Interpolator AGGRESSIVE_EASE = new PathInterpolator(0.2f, 0f, 0f, 1f); public static final Interpolator AGGRESSIVE_EASE_IN_OUT = new PathInterpolator(0.6f,0, 0.4f, 1); diff --git a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java index 9ac1c0e4f9..2a0fe3aea2 100644 --- a/src/com/android/launcher3/touch/LandscapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/LandscapePagedViewHandler.java @@ -440,7 +440,7 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight, int splitInstructionsWidth, int threeButtonNavShift) { out.setPivotX(0); - out.setPivotY(0); + out.setPivotY(splitInstructionsHeight); out.setRotation(getDegreesRotated()); int distanceToEdge = out.getResources().getDimensionPixelSize( R.dimen.split_instructions_bottom_margin_phone_landscape); @@ -448,8 +448,8 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler { int insetCorrectionX = dp.getInsets().left; // Center the view in case of unbalanced insets on top or bottom of screen int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2; - out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX); - out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f) + out.setTranslationX(distanceToEdge - insetCorrectionX); + out.setTranslationY(((-splitInstructionsHeight - splitInstructionsWidth) / 2f) + insetCorrectionY); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams(); // Setting gravity to LEFT instead of the lint-recommended START because we always want this diff --git a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java index dd9f64266e..f89c0e52b0 100644 --- a/src/com/android/launcher3/touch/PortraitPagedViewHandler.java +++ b/src/com/android/launcher3/touch/PortraitPagedViewHandler.java @@ -500,7 +500,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler { public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight, int splitInstructionsWidth, int threeButtonNavShift) { out.setPivotX(0); - out.setPivotY(0); + out.setPivotY(splitInstructionsHeight); out.setRotation(getDegreesRotated()); int distanceToEdge; if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS) diff --git a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java index 387e980320..55bb5e85e5 100644 --- a/src/com/android/launcher3/touch/SeascapePagedViewHandler.java +++ b/src/com/android/launcher3/touch/SeascapePagedViewHandler.java @@ -190,7 +190,7 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight, int splitInstructionsWidth, int threeButtonNavShift) { out.setPivotX(0); - out.setPivotY(0); + out.setPivotY(splitInstructionsHeight); out.setRotation(getDegreesRotated()); int distanceToEdge = out.getResources().getDimensionPixelSize( R.dimen.split_instructions_bottom_margin_phone_landscape); @@ -198,9 +198,8 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler { int insetCorrectionX = dp.getInsets().right; // Center the view in case of unbalanced insets on top or bottom of screen int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2; - out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge - + insetCorrectionX); - out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f) + out.setTranslationX(splitInstructionsWidth - distanceToEdge + insetCorrectionX); + out.setTranslationY(((-splitInstructionsHeight + splitInstructionsWidth) / 2f) + insetCorrectionY); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams(); // Setting gravity to RIGHT instead of the lint-recommended END because we always want this