From 5dc07d786fcb910557747d049a6f7857bcc16c75 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Thu, 25 Jul 2024 07:57:00 -0700 Subject: [PATCH] Add support for taskbar background to wrap around hotseat Currently only enabled for app launch animation Future work is planned: - Overview to home - App dismiss - When QSB is drawn inline Bug: 345768019 Test: Launch an app, taskbar background and stashed handle will first wrap around hotseat before transforming into the stashed handle. Verified by locally forcing taskbar background and stashed handle to always draw. Flag: com.android.launcher3.enable_scaling_reveal_home_animation Change-Id: I9ab1870f87247b6a1b53a352ac3eb0183b7a1a1d --- .../taskbar/LauncherTaskbarUIController.java | 19 ++++++++ .../taskbar/StashedHandleViewController.java | 12 ++++- .../taskbar/TaskbarActivityContext.java | 44 +++++++++++++++++++ .../taskbar/TaskbarBackgroundRenderer.kt | 18 ++++++-- .../launcher3/taskbar/TaskbarDragLayer.java | 6 +++ .../taskbar/TaskbarStashController.java | 24 ++++++++-- .../taskbar/TaskbarViewController.java | 21 +++++++-- src/com/android/launcher3/DeviceProfile.java | 1 - 8 files changed, 133 insertions(+), 12 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index 779009a6b0..5391a7909c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -24,6 +24,7 @@ import static com.android.window.flags.Flags.enableDesktopWindowingWallpaperActi import android.animation.Animator; import android.animation.AnimatorSet; +import android.graphics.Rect; import android.window.RemoteTransition; import androidx.annotation.NonNull; @@ -179,6 +180,24 @@ public class LauncherTaskbarUIController extends TaskbarUIController { placeholderDuration)); } + /** + * Returns the bounds of launcher's hotseat. + */ + public void getHotseatBounds(Rect hotseatBoundsOut) { + DeviceProfile launcherDP = mLauncher.getDeviceProfile(); + if (launcherDP.isQsbInline) { + // Not currently supported. + hotseatBoundsOut.setEmpty(); + return; + } + int left = (launcherDP.widthPx - launcherDP.getHotseatWidthPx() + - mLauncher.getHotseat().getUnusedHorizontalSpace()) / 2; + int right = left + launcherDP.getHotseatWidthPx(); + int bottom = launcherDP.getHotseatLayoutPadding(mLauncher).bottom; + int top = bottom - launcherDP.hotseatCellHeightPx; + hotseatBoundsOut.set(left, top, right, bottom); + } + /** * Should be called from onResume() and onPause(), and animates the Taskbar accordingly. */ diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java index 252f2a81f0..266d0b9c98 100644 --- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java @@ -207,8 +207,11 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT * Creates and returns a {@link RevealOutlineAnimation} Animator that updates the stashed handle * shape and size. When stashed, the shape is a thin rounded pill. When unstashed, the shape * morphs into the size of where the taskbar icons will be. + * + * @param taskbarToHotseatOffsets A Rect of offsets used to transform the bounds of the + * stashed handle to wrap around the hotseat items. */ - public Animator createRevealAnimToIsStashed(boolean isStashed) { + public Animator createRevealAnimToIsStashed(boolean isStashed, Rect taskbarToHotseatOffsets) { Rect visualBounds = new Rect(mControllers.taskbarViewController.getIconLayoutBounds()); float startRadius = mStashedHandleRadius; @@ -219,6 +222,13 @@ public class StashedHandleViewController implements TaskbarControllers.LoggableT visualBounds.bottom += heightDiff; startRadius = visualBounds.height() / 2f; + + // We use these offsets to create a larger stashed handle to wrap around the items + // of the hotseat. This is only used for certain animations. + visualBounds.top += taskbarToHotseatOffsets.top; + visualBounds.bottom += taskbarToHotseatOffsets.bottom; + visualBounds.left += taskbarToHotseatOffsets.left; + visualBounds.right += taskbarToHotseatOffsets.right; } final RevealOutlineAnimation handleRevealProvider = new RoundedRectRevealOutlineProvider( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 0de0550016..4534860e4b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -336,6 +336,38 @@ public class TaskbarActivityContext extends BaseTaskbarContext { dispatchDeviceProfileChanged(); } + /** + * Calculate the offsets needed to transform the transient taskbar bounds to the hotseat bounds. + * @return The offsets will be stored in a Rect + */ + public Rect calculateTaskbarToHotseatOffsets(Rect hotseatBounds) { + Rect taskbar = getTransientTaskbarBounds(); + Rect offsets = new Rect(); + + offsets.left = hotseatBounds.left - taskbar.left; + offsets.right = hotseatBounds.right - taskbar.right; + + int heightDiff = hotseatBounds.height() - taskbar.height(); + offsets.top = (taskbar.height() - heightDiff) / 2; + + int gleanedTaskbarPadding = (mDeviceProfile.taskbarHeight + - getTransientTaskbarBounds().height()) / 2; + offsets.left -= gleanedTaskbarPadding; + offsets.top -= gleanedTaskbarPadding; + offsets.right += gleanedTaskbarPadding; + + // Bottom is relative to the bottom of layout, so we can calculate it with padding included. + offsets.bottom = (hotseatBounds.height() - taskbar.height()) / 2; + + // Update bounds in taskbar background + if (hotseatBounds.isEmpty()) { + mDragLayer.getTaskbarToHotseatOffsetRect().setEmpty(); + } else { + mDragLayer.getTaskbarToHotseatOffsetRect().set(offsets); + } + return offsets; + } + /** * Copy the original DeviceProfile, match the number of hotseat icons and qsb width and update * the icon size @@ -1517,6 +1549,18 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return AnimatorPlaybackController.wrap(fullAnimation, duration); } + /** + * Returns the bounds of launcher's hotseat (if exists). + */ + public void getHotseatBounds(Rect hotseatBoundsOut) { + TaskbarUIController uiController = mControllers.uiController; + if (uiController instanceof LauncherTaskbarUIController launcherController) { + launcherController.getHotseatBounds(hotseatBoundsOut); + } else { + hotseatBoundsOut.setEmpty(); + } + } + /** * Called when we determine the touchable region. * diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt index bafd05924f..bc609b4bde 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt @@ -21,6 +21,7 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path +import android.graphics.Rect import android.graphics.RectF import com.android.app.animation.Interpolators import com.android.launcher3.R @@ -59,6 +60,9 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { var translationYForSwipe = 0f var translationYForStash = 0f + // When not empty, we can use this to transform transient taskbar background to hotseat bounds. + val taskbarToHotseatOffsetRect = Rect() + private val transientBackgroundBounds = context.transientTaskbarBounds private val shadowAlpha: Float @@ -215,6 +219,12 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { val radius = newBackgroundHeight / 2f val bottomMarginProgress = bottomMargin * ((1f - progress) / 2f) + // Used to transform the background so that it wraps around the items on the hotseat. + val hotseatOffsetLeft = taskbarToHotseatOffsetRect.left * progress + val hotseatOffsetTop = taskbarToHotseatOffsetRect.top * progress + val hotseatOffsetRight = taskbarToHotseatOffsetRect.right * progress + val hotseatOffsetBottom = taskbarToHotseatOffsetRect.bottom * progress + // Aligns the bottom with the bottom of the stashed handle. val bottom = canvas.height - bottomMargin + @@ -238,10 +248,10 @@ class TaskbarBackgroundRenderer(private val context: TaskbarActivityContext) { ) lastDrawnTransientRect.set( - transientBackgroundBounds.left + halfWidthDelta, - bottom - newBackgroundHeight, - transientBackgroundBounds.right - halfWidthDelta, - bottom + transientBackgroundBounds.left + halfWidthDelta + hotseatOffsetLeft, + bottom - newBackgroundHeight + hotseatOffsetTop, + transientBackgroundBounds.right - halfWidthDelta + hotseatOffsetRight, + bottom + hotseatOffsetBottom ) val horizontalInset = fullWidth * widthInsetPercentage lastDrawnTransientRect.inset(horizontalInset, 0f) diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java index 84874a9371..e7d2d1077c 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayer.java @@ -22,6 +22,7 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UN import android.content.Context; import android.graphics.Canvas; +import android.graphics.Rect; import android.graphics.RectF; import android.media.permission.SafeCloseable; import android.util.AttributeSet; @@ -260,6 +261,11 @@ public class TaskbarDragLayer extends BaseDragLayer { return mBackgroundRenderer.getLastDrawnTransientRect(); } + /** Returns the rect used to transform transient taskbar to the hotseat */ + public Rect getTaskbarToHotseatOffsetRect() { + return mBackgroundRenderer.getTaskbarToHotseatOffsetRect(); + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index 74d2d60014..59320381f2 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -22,6 +22,7 @@ import static com.android.app.animation.Interpolators.FINAL_FRAME; import static com.android.app.animation.Interpolators.INSTANT; import static com.android.app.animation.Interpolators.LINEAR; import static com.android.internal.jank.InteractionJankMonitor.Configuration; +import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_SHOW; @@ -41,6 +42,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.app.RemoteAction; +import android.graphics.Rect; import android.graphics.drawable.Icon; import android.os.SystemClock; import android.util.Log; @@ -196,6 +198,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * by not scaling the height of the taskbar background. */ private static final int TRANSITION_UNSTASH_SUW_MANUAL = 3; + private static final Rect EMPTY_RECT = new Rect(); @Retention(RetentionPolicy.SOURCE) @IntDef(value = { @@ -698,7 +701,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } fullLengthAnimatorSet.play(mControllers.stashedHandleViewController - .createRevealAnimToIsStashed(isStashed)); + .createRevealAnimToIsStashed(isStashed, EMPTY_RECT)); // Return the stashed handle to its default scale in case it was changed as part of the // feedforward hint. Note that the reveal animation above also visually scales it. fullLengthAnimatorSet.play(mTaskbarStashedHandleHintScale.animateToValue(1f)); @@ -748,6 +751,19 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } } + + Rect taskbarToHotseatOffsets = new Rect(); + if (enableScalingRevealHomeAnimation() && animationType == TRANSITION_HOME_TO_APP) { + Rect hotseatRect = new Rect(); + mActivity.getHotseatBounds(hotseatRect); + + // Calculate and store offsets so that we can sync with the taskbar stashed handle + taskbarToHotseatOffsets.set( + mActivity.calculateTaskbarToHotseatOffsets(hotseatRect)); + as.addListener(AnimatorListeners.forEndCallback( + () -> mActivity.calculateTaskbarToHotseatOffsets(EMPTY_RECT))); + } + play(as, mTaskbarStashedHandleAlpha.animateToValue(stashedHandleAlphaTarget), backgroundAndHandleAlphaStartDelay, backgroundAndHandleAlphaDuration, LINEAR); @@ -786,10 +802,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba } mControllers.taskbarViewController.addRevealAnimToIsStashed(skippable, isStashed, duration, - EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL); + EMPHASIZED, animationType == TRANSITION_UNSTASH_SUW_MANUAL, + animationType == TRANSITION_HOME_TO_APP); play(skippable, mControllers.stashedHandleViewController - .createRevealAnimToIsStashed(isStashed), 0, duration, EMPHASIZED); + .createRevealAnimToIsStashed(isStashed, taskbarToHotseatOffsets), 0, duration, + EMPHASIZED); // Return the stashed handle to its default scale in case it was changed as part of the // feedforward hint. Note that the reveal animation above also visually scales it. diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index 23495adcf7..f8e205b916 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -33,6 +33,7 @@ import static com.android.launcher3.util.MultiPropertyFactory.MULTI_PROPERTY_VAL import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_ALIGNMENT_ANIM; import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_PINNING_ANIM; import static com.android.launcher3.util.MultiTranslateDelegate.INDEX_TASKBAR_REVEAL_ANIM; +import static com.android.launcher3.util.window.RefreshRateTracker.getSingleFrameMs; import android.animation.Animator; import android.animation.AnimatorSet; @@ -551,7 +552,8 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * @param interpolator The interpolator to use for all animations. */ public void addRevealAnimToIsStashed(AnimatorSet as, boolean isStashed, long duration, - Interpolator interpolator, boolean dispatchOnAnimationStart) { + Interpolator interpolator, boolean dispatchOnAnimationStart, + boolean isHomeToAppAnimation) { AnimatorSet reveal = new AnimatorSet(); Rect stashedBounds = new Rect(); @@ -600,8 +602,21 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar reveal.play(ObjectAnimator.ofFloat(mtd.getTranslationX(INDEX_TASKBAR_REVEAL_ANIM), MULTI_PROPERTY_VALUE, transX) .setDuration(duration)); - reveal.play(ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM), - MULTI_PROPERTY_VALUE, transY)); + + if (enableScalingRevealHomeAnimation()) { + // Delay y-translation by 1 frame to keep icons within the bounds of the bg. + int delay = isHomeToAppAnimation ? getSingleFrameMs(mActivity) : 0; + ObjectAnimator yAnimator = + ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM), + MULTI_PROPERTY_VALUE, transY) + .setDuration(Math.max(0, duration - delay)); + yAnimator.setStartDelay(delay); + reveal.play(yAnimator); + } else { + reveal.play( + ObjectAnimator.ofFloat(mtd.getTranslationY(INDEX_TASKBAR_REVEAL_ANIM), + MULTI_PROPERTY_VALUE, transY)); + } as.addListener(forEndCallback(() -> mtd.setTranslation(INDEX_TASKBAR_REVEAL_ANIM, 0, 0))); } else { diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 0daabb1451..3a81b8f774 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -994,7 +994,6 @@ public class DeviceProfile { return mHotseatColumnSpan; } - @VisibleForTesting public int getHotseatWidthPx() { return mHotseatWidthPx; }