diff options
12 files changed, 375 insertions, 86 deletions
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 87324dba64de..58ee2b21296b 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -19,6 +19,7 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Trace.TRACE_TAG_APP; import static android.provider.DeviceConfig.NAMESPACE_LATENCY_TRACKER; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL; @@ -228,6 +229,11 @@ public class LatencyTracker { */ public static final int ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME = 24; + /** + * Time it takes to start back preview surface animation after a back gesture starts. + */ + public static final int ACTION_BACK_SYSTEM_ANIMATION = 25; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -254,6 +260,7 @@ public class LatencyTracker { ACTION_SMARTSPACE_DOORBELL, ACTION_NOTIFICATION_BIG_PICTURE_LOADED, ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, + ACTION_BACK_SYSTEM_ANIMATION, }; /** @hide */ @@ -283,6 +290,7 @@ public class LatencyTracker { ACTION_SMARTSPACE_DOORBELL, ACTION_NOTIFICATION_BIG_PICTURE_LOADED, ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, + ACTION_BACK_SYSTEM_ANIMATION, }) @Retention(RetentionPolicy.SOURCE) public @interface Action { @@ -315,6 +323,7 @@ public class LatencyTracker { UIACTION_LATENCY_REPORTED__ACTION__ACTION_SMARTSPACE_DOORBELL, UIACTION_LATENCY_REPORTED__ACTION__ACTION_NOTIFICATION_BIG_PICTURE_LOADED, UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION, }; private final Object mLock = new Object(); @@ -503,6 +512,8 @@ public class LatencyTracker { return "ACTION_NOTIFICATION_BIG_PICTURE_LOADED"; case UIACTION_LATENCY_REPORTED__ACTION__ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME: return "ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_BACK_SYSTEM_ANIMATION: + return "ACTION_BACK_SYSTEM_ANIMATION"; default: throw new IllegalArgumentException("Invalid action"); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 03c546dd2cf3..58436351885a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -61,6 +61,7 @@ import android.window.IOnBackInvokedCallback; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.LatencyTracker; import com.android.internal.view.AppearanceRegion; import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.ExternalInterfaceBinder; @@ -99,6 +100,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont * Max duration to wait for an animation to finish before triggering the real back. */ private static final long MAX_ANIMATION_DURATION = 2000; + private final LatencyTracker mLatencyTracker; /** True when a back gesture is ongoing */ private boolean mBackGestureStarted = false; @@ -167,6 +169,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private final BackAnimationBackground mAnimationBackground; private StatusBarCustomizer mCustomizer; + private boolean mTrackingLatency; public BackAnimationController( @NonNull ShellInit shellInit, @@ -213,6 +216,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont .setSpeedUpFactor(FLING_SPEED_UP_FACTOR) .build(); mShellBackAnimationRegistry = shellBackAnimationRegistry; + mLatencyTracker = LatencyTracker.getInstance(mContext); } private void onInit() { @@ -438,6 +442,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private void startBackNavigation(@NonNull TouchTracker touchTracker) { try { + startLatencyTracking(); mBackNavigationInfo = mActivityTaskManager.startBackNavigation( mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null); onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker); @@ -452,6 +457,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo); if (backNavigationInfo == null) { ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Received BackNavigationInfo is null."); + cancelLatencyTracking(); return; } final int backType = backNavigationInfo.getType(); @@ -462,6 +468,8 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont } } else { mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback(); + // App is handling back animation. Cancel system animation latency tracking. + cancelLatencyTracking(); dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); } } @@ -808,12 +816,36 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()"); mActiveCallback = null; mShellBackAnimationRegistry.resetDefaultCrossActivity(); + cancelLatencyTracking(); if (mBackNavigationInfo != null) { mBackNavigationInfo.onBackNavigationFinished(triggerBack); mBackNavigationInfo = null; } } + private void startLatencyTracking() { + if (mTrackingLatency) { + cancelLatencyTracking(); + } + mLatencyTracker.onActionStart(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); + mTrackingLatency = true; + } + + private void cancelLatencyTracking() { + if (!mTrackingLatency) { + return; + } + mLatencyTracker.onActionCancel(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); + mTrackingLatency = false; + } + + private void endLatencyTracking() { + if (!mTrackingLatency) { + return; + } + mLatencyTracker.onActionEnd(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION); + mTrackingLatency = false; + } private void createAdapter() { IBackAnimationRunner runner = @@ -826,6 +858,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont IBackAnimationFinishedCallback finishedCallback) { mShellExecutor.execute( () -> { + endLatencyTracking(); if (mBackNavigationInfo == null) { ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Lack of navigation info to start animation."); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java index 1941d66cc172..652a2ed39c67 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.java @@ -70,8 +70,8 @@ class HandleMenu { private int mMarginMenuStart; private int mMenuHeight; private int mMenuWidth; - private final int mCaptionHeight; + private HandleMenuAnimator mHandleMenuAnimator; HandleMenu(WindowDecoration parentDecor, int layoutResId, int captionX, int captionY, @@ -111,20 +111,19 @@ class HandleMenu { mHandleMenuWindow = mParentDecor.addWindow( R.layout.desktop_mode_window_decor_handle_menu, "Handle Menu", t, ssg, x, y, mMenuWidth, mMenuHeight); + final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView(); + mHandleMenuAnimator = new HandleMenuAnimator(handleMenuView, mMenuWidth, mCaptionHeight); } /** * Animates the appearance of the handle menu and its three pills. */ private void animateHandleMenu() { - final View handleMenuView = mHandleMenuWindow.mWindowViewHost.getView(); - final HandleMenuAnimator handleMenuAnimator = new HandleMenuAnimator(handleMenuView, - mMenuWidth, mCaptionHeight); if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { - handleMenuAnimator.animateCaptionHandleExpandToOpen(); + mHandleMenuAnimator.animateCaptionHandleExpandToOpen(); } else { - handleMenuAnimator.animateOpen(); + mHandleMenuAnimator.animateOpen(); } } @@ -328,8 +327,16 @@ class HandleMenu { } void close() { - mHandleMenuWindow.releaseView(); - mHandleMenuWindow = null; + final Runnable after = () -> { + mHandleMenuWindow.releaseView(); + mHandleMenuWindow = null; + }; + if (mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN + || mTaskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) { + mHandleMenuAnimator.animateCollapseIntoHandleClose(after); + } else { + mHandleMenuAnimator.animateClose(after); + } } static final class Builder { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt index 531de1f79ea8..8c5d4a2c2ffb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuAnimator.kt @@ -26,6 +26,7 @@ import android.view.View.SCALE_Y import android.view.View.TRANSLATION_Y import android.view.View.TRANSLATION_Z import android.view.ViewGroup +import androidx.core.animation.doOnEnd import androidx.core.view.children import com.android.wm.shell.R import com.android.wm.shell.animation.Interpolators @@ -37,27 +38,36 @@ class HandleMenuAnimator( private val captionHeight: Float ) { companion object { - private const val MENU_Y_TRANSLATION_DURATION: Long = 150 - private const val HEADER_NONFREEFORM_SCALE_DURATION: Long = 150 - private const val HEADER_FREEFORM_SCALE_DURATION: Long = 217 - private const val HEADER_ELEVATION_DURATION: Long = 83 - private const val HEADER_CONTENT_ALPHA_DURATION: Long = 100 - private const val BODY_SCALE_DURATION: Long = 180 - private const val BODY_ALPHA_DURATION: Long = 150 - private const val BODY_ELEVATION_DURATION: Long = 83 - private const val BODY_CONTENT_ALPHA_DURATION: Long = 167 - - private const val ELEVATION_DELAY: Long = 33 - private const val HEADER_CONTENT_ALPHA_DELAY: Long = 67 - private const val BODY_SCALE_DELAY: Long = 50 - private const val BODY_ALPHA_DELAY: Long = 133 + // Open animation constants + private const val MENU_Y_TRANSLATION_OPEN_DURATION: Long = 150 + private const val HEADER_NONFREEFORM_SCALE_OPEN_DURATION: Long = 150 + private const val HEADER_FREEFORM_SCALE_OPEN_DURATION: Long = 217 + private const val HEADER_ELEVATION_OPEN_DURATION: Long = 83 + private const val HEADER_CONTENT_ALPHA_OPEN_DURATION: Long = 100 + private const val BODY_SCALE_OPEN_DURATION: Long = 180 + private const val BODY_ALPHA_OPEN_DURATION: Long = 150 + private const val BODY_ELEVATION_OPEN_DURATION: Long = 83 + private const val BODY_CONTENT_ALPHA_OPEN_DURATION: Long = 167 + + private const val ELEVATION_OPEN_DELAY: Long = 33 + private const val HEADER_CONTENT_ALPHA_OPEN_DELAY: Long = 67 + private const val BODY_SCALE_OPEN_DELAY: Long = 50 + private const val BODY_ALPHA_OPEN_DELAY: Long = 133 private const val HALF_INITIAL_SCALE: Float = 0.5f private const val NONFREEFORM_HEADER_INITIAL_SCALE_X: Float = 0.6f private const val NONFREEFORM_HEADER_INITIAL_SCALE_Y: Float = 0.05f + + // Close animation constants + private const val HEADER_CLOSE_DELAY: Long = 20 + private const val HEADER_CLOSE_DURATION: Long = 50 + private const val HEADER_CONTENT_OPACITY_CLOSE_DELAY: Long = 25 + private const val HEADER_CONTENT_OPACITY_CLOSE_DURATION: Long = 25 + private const val BODY_CLOSE_DURATION: Long = 50 } private val animators: MutableList<Animator> = mutableListOf() + private var runningAnimation: AnimatorSet? = null private val appInfoPill: ViewGroup = handleMenu.requireViewById(R.id.app_info_pill) private val windowingPill: ViewGroup = handleMenu.requireViewById(R.id.windowing_pill) @@ -67,9 +77,9 @@ class HandleMenuAnimator( fun animateOpen() { prepareMenuForAnimation() appInfoPillExpand() - animateAppInfoPill() - animateWindowingPill() - animateMoreActionsPill() + animateAppInfoPillOpen() + animateWindowingPillOpen() + animateMoreActionsPillOpen() runAnimations() } @@ -81,13 +91,44 @@ class HandleMenuAnimator( fun animateCaptionHandleExpandToOpen() { prepareMenuForAnimation() captionHandleExpandIntoAppInfoPill() - animateAppInfoPill() - animateWindowingPill() - animateMoreActionsPill() + animateAppInfoPillOpen() + animateWindowingPillOpen() + animateMoreActionsPillOpen() runAnimations() } /** + * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then, + * the app info pill will collapse into the shape of the caption handle in full screen and split + * screen. + * + * @param after runs after the animation finishes. + */ + fun animateCollapseIntoHandleClose(after: Runnable) { + appInfoCollapseToHandle() + animateAppInfoPillFadeOut() + windowingPillClose() + moreActionsPillClose() + runAnimations(after) + } + + /** + * Animates the closing of the handle menu. The windowing and more actions pill vanish. Then, + * the app info pill will collapse into the shape of the caption handle in full screen and split + * screen. + * + * @param after runs after animation finishes. + * + */ + fun animateClose(after: Runnable) { + appInfoPillCollapse() + animateAppInfoPillFadeOut() + windowingPillClose() + moreActionsPillClose() + runAnimations(after) + } + + /** * Prepares the handle menu for animation. Presets the opacity of necessary menu components. * Presets pivots of handle menu and body pills for scaling animation. */ @@ -108,20 +149,20 @@ class HandleMenuAnimator( moreActionsPill.pivotY = appInfoPill.measuredHeight.toFloat() } - private fun animateAppInfoPill() { + private fun animateAppInfoPillOpen() { // Header Elevation Animation animators += ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Z, 1f).apply { - startDelay = ELEVATION_DELAY - duration = HEADER_ELEVATION_DURATION + startDelay = ELEVATION_OPEN_DELAY + duration = HEADER_ELEVATION_OPEN_DURATION } // Content Opacity Animation appInfoPill.children.forEach { animators += ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { - startDelay = HEADER_CONTENT_ALPHA_DELAY - duration = HEADER_CONTENT_ALPHA_DURATION + startDelay = HEADER_CONTENT_ALPHA_OPEN_DELAY + duration = HEADER_CONTENT_ALPHA_OPEN_DURATION } } } @@ -130,17 +171,17 @@ class HandleMenuAnimator( // Header scaling animation animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X, 1f) - .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION } + .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y, 1f) - .apply { duration = HEADER_NONFREEFORM_SCALE_DURATION } + .apply { duration = HEADER_NONFREEFORM_SCALE_OPEN_DURATION } // Downward y-translation animation val yStart: Float = -captionHeight / 2 animators += ObjectAnimator.ofFloat(handleMenu, TRANSLATION_Y, yStart, 0f).apply { - duration = MENU_Y_TRANSLATION_DURATION + duration = MENU_Y_TRANSLATION_OPEN_DURATION } } @@ -148,98 +189,217 @@ class HandleMenuAnimator( // Header scaling animation animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { - duration = HEADER_FREEFORM_SCALE_DURATION + duration = HEADER_FREEFORM_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { - duration = HEADER_FREEFORM_SCALE_DURATION + duration = HEADER_FREEFORM_SCALE_OPEN_DURATION } } - private fun animateWindowingPill() { + private fun animateWindowingPillOpen() { // Windowing X & Y Scaling Animation animators += ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } // Windowing Opacity Animation animators += ObjectAnimator.ofFloat(windowingPill, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_ALPHA_OPEN_DURATION } // Windowing Elevation Animation animators += ObjectAnimator.ofFloat(windowingPill, TRANSLATION_Z, 1f).apply { - startDelay = ELEVATION_DELAY - duration = BODY_ELEVATION_DURATION + startDelay = ELEVATION_OPEN_DELAY + duration = BODY_ELEVATION_OPEN_DURATION } // Windowing Content Opacity Animation windowingPill.children.forEach { animators += ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_CONTENT_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_CONTENT_ALPHA_OPEN_DURATION interpolator = Interpolators.FAST_OUT_SLOW_IN } } } - private fun animateMoreActionsPill() { + private fun animateMoreActionsPillOpen() { // More Actions X & Y Scaling Animation animators += ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } animators += ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE, 1f).apply { - startDelay = BODY_SCALE_DELAY - duration = BODY_SCALE_DURATION + startDelay = BODY_SCALE_OPEN_DELAY + duration = BODY_SCALE_OPEN_DURATION } // More Actions Opacity Animation animators += ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_ALPHA_OPEN_DURATION } // More Actions Elevation Animation animators += ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Z, 1f).apply { - startDelay = ELEVATION_DELAY - duration = BODY_ELEVATION_DURATION + startDelay = ELEVATION_OPEN_DELAY + duration = BODY_ELEVATION_OPEN_DURATION } // More Actions Content Opacity Animation moreActionsPill.children.forEach { animators += ObjectAnimator.ofFloat(it, ALPHA, 1f).apply { - startDelay = BODY_ALPHA_DELAY - duration = BODY_CONTENT_ALPHA_DURATION + startDelay = BODY_ALPHA_OPEN_DELAY + duration = BODY_CONTENT_ALPHA_OPEN_DURATION interpolator = Interpolators.FAST_OUT_SLOW_IN } } } - /** Runs the list of animators concurrently. */ - private fun runAnimations() { - val animatorSet = AnimatorSet() - animatorSet.playTogether(animators) - animatorSet.start() - animators.clear() + private fun appInfoPillCollapse() { + // Header scaling animation + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_X, 0f).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, 0f).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + } + + private fun appInfoCollapseToHandle() { + // Header X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_X, NONFREEFORM_HEADER_INITIAL_SCALE_X).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(appInfoPill, SCALE_Y, NONFREEFORM_HEADER_INITIAL_SCALE_Y).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + // Upward y-translation animation + val yStart: Float = -captionHeight / 2 + animators += + ObjectAnimator.ofFloat(appInfoPill, TRANSLATION_Y, yStart).apply { + startDelay = HEADER_CLOSE_DELAY + duration = HEADER_CLOSE_DURATION + } + } + + private fun animateAppInfoPillFadeOut() { + // Header Content Opacity Animation + appInfoPill.children.forEach { + animators += + ObjectAnimator.ofFloat(it, ALPHA, 0f).apply { + startDelay = HEADER_CONTENT_OPACITY_CLOSE_DELAY + duration = HEADER_CONTENT_OPACITY_CLOSE_DURATION + } + } + } + + private fun windowingPillClose() { + // Windowing X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(windowingPill, SCALE_X, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(windowingPill, SCALE_Y, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + // windowing Animation + animators += + ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(windowingPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + } + + private fun moreActionsPillClose() { + // More Actions X & Y Scaling Animation + animators += + ObjectAnimator.ofFloat(moreActionsPill, SCALE_X, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(moreActionsPill, SCALE_Y, HALF_INITIAL_SCALE).apply { + duration = BODY_CLOSE_DURATION + } + + // More Actions Opacity Animation + animators += + ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + animators += + ObjectAnimator.ofFloat(moreActionsPill, ALPHA, 0f).apply { + duration = BODY_CLOSE_DURATION + } + + // upward more actions pill y-translation animation + val yStart: Float = -captionHeight / 2 + animators += + ObjectAnimator.ofFloat(moreActionsPill, TRANSLATION_Y, yStart).apply { + duration = BODY_CLOSE_DURATION + } + } + + /** + * Runs the list of hide animators concurrently. + * + * @param after runs after animation finishes. + */ + private fun runAnimations(after: Runnable? = null) { + runningAnimation?.apply { + // Remove all listeners, so that after runnable isn't triggered upon cancel. + removeAllListeners() + // If an animation runs while running animation is triggered, gracefully cancel. + cancel() + } + + runningAnimation = AnimatorSet().apply { + playTogether(animators) + animators.clear() + doOnEnd { + after?.run() + runningAnimation = null + } + start() + } } } diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt new file mode 100644 index 000000000000..de46f7209c84 --- /dev/null +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestSceneScope.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.compose.animation.scene + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier + +/** `SceneScope` for tests, which allows a single scene to be drawn in a [SceneTransitionLayout]. */ +@Composable +fun TestSceneScope( + modifier: Modifier = Modifier, + content: @Composable SceneScope.() -> Unit, +) { + val currentScene = remember { SceneKey("current") } + SceneTransitionLayout( + currentScene, + onChangeScene = { /* do nothing */}, + transitions = remember { transitions {} }, + modifier, + ) { + scene(currentScene, content = content) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java index 72935f77af52..d92b5060a5d0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/IRadiiAnimationListener.java @@ -18,4 +18,8 @@ package com.android.systemui.accessibility.floatingmenu; interface IRadiiAnimationListener { void onRadiiAnimationUpdate(float[] radii); + + void onRadiiAnimationStart(); + + void onRadiiAnimationStop(); } diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java index 761551cbd906..34d7cecc47b0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationController.java @@ -94,7 +94,19 @@ class MenuAnimationController { mFadeOutAnimator.setDuration(FADE_OUT_DURATION_MS); mFadeOutAnimator.addUpdateListener( (animation) -> menuView.setAlpha((float) animation.getAnimatedValue())); - mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), mMenuView::setRadii); + mRadiiAnimator = new RadiiAnimator(mMenuViewAppearance.getMenuRadii(), + new IRadiiAnimationListener() { + @Override + public void onRadiiAnimationUpdate(float[] radii) { + mMenuView.setRadii(radii); + } + + @Override + public void onRadiiAnimationStart() {} + + @Override + public void onRadiiAnimationStop() {} + }); } void moveToPosition(PointF position) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java index acad36ec49b8..4aa0d89cddb5 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimator.java @@ -55,15 +55,19 @@ class RadiiAnimator { @Override public void onAnimationStart(@NonNull Animator animation) { animationListener.onRadiiAnimationUpdate(evaluate(/* t = */ 0.0f)); + animationListener.onRadiiAnimationStart(); } @Override - public void onAnimationEnd(@NonNull Animator animation) {} + public void onAnimationEnd(@NonNull Animator animation) { + animationListener.onRadiiAnimationStop(); + } @Override public void onAnimationCancel(@NonNull Animator animation) { animationListener.onRadiiAnimationUpdate( evaluate(mAnimationDriver.getAnimatedFraction())); + animationListener.onRadiiAnimationStop(); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java index e3a2c59b3c6c..d77a80a3318d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/RadiiAnimatorTest.java @@ -31,42 +31,60 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; /** Tests for {@link RadiiAnimator}. */ @SmallTest @RunWith(AndroidTestingRunner.class) public class RadiiAnimatorTest extends SysuiTestCase { float[] mResultRadii = new float[RadiiAnimator.RADII_COUNT]; + final AtomicBoolean mAnimationStarted = new AtomicBoolean(false); + final AtomicBoolean mAnimationStopped = new AtomicBoolean(false); + final IRadiiAnimationListener mRadiiAnimationListener = new IRadiiAnimationListener() { + @Override + public void onRadiiAnimationUpdate(float[] radii) { + mResultRadii = radii; + } + + @Override + public void onRadiiAnimationStart() { + mAnimationStarted.set(true); + } + + @Override + public void onRadiiAnimationStop() { + mAnimationStopped.set(true); + } + }; @Test public void constructor() { final float[] radii = generateRadii(0.0f); - final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, newRadii -> {}); - + final RadiiAnimator radiiAnimator = new RadiiAnimator(radii, mRadiiAnimationListener); assertThat(radiiAnimator.evaluate(0.0f)).isEqualTo(radii); } @Test - public void skip_updates_to_end() { + public void skipAnimation_updatesToEnd() { final float[] startRadii = generateRadii(0.0f); final float[] endRadii = generateRadii(1.0f); final RadiiAnimator radiiAnimator = setupAnimator(startRadii); + mAnimationStarted.set(false); + mAnimationStopped.set(false); new Handler(Looper.getMainLooper()).post(() -> radiiAnimator.startAnimation(endRadii)); - TestUtils.waitForCondition(radiiAnimator::isStarted, "Animation did not start."); + TestUtils.waitForCondition(mAnimationStarted::get, "Animation did not start."); TestUtils.waitForCondition(() -> Arrays.equals(radiiAnimator.evaluate(0.0f), startRadii) - && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii), + && Arrays.equals(radiiAnimator.evaluate(1.0f), endRadii), "Animator did not initialize to start and end values"); - new Handler(Looper.getMainLooper()).post(radiiAnimator::skipAnimationToEnd); - TestUtils.waitForCondition( - () -> !radiiAnimator.isStarted(), "Animation did not end."); + TestUtils.waitForCondition(mAnimationStopped::get, "Animation did not stop."); assertThat(mResultRadii).usingTolerance(0.001).containsExactly(endRadii); } @Test - public void animation_can_repeat() { + public void finishedAnimation_canRepeat() { final float[] startRadii = generateRadii(0.0f); final float[] midRadii = generateRadii(1.0f); final float[] endRadii = generateRadii(2.0f); @@ -88,15 +106,15 @@ public class RadiiAnimatorTest extends SysuiTestCase { private RadiiAnimator setupAnimator(float[] startRadii) { mResultRadii = new float[RadiiAnimator.RADII_COUNT]; - return new RadiiAnimator(startRadii, - newRadii -> mResultRadii = newRadii); + return new RadiiAnimator(startRadii, mRadiiAnimationListener); } private void playAndSkipAnimation(RadiiAnimator animator, float[] endRadii) { + mAnimationStarted.set(false); + mAnimationStopped.set(false); new Handler(Looper.getMainLooper()).post(() -> animator.startAnimation(endRadii)); - TestUtils.waitForCondition(animator::isStarted, "Animation did not start."); + TestUtils.waitForCondition(mAnimationStarted::get, "Animation did not start."); new Handler(Looper.getMainLooper()).post(animator::skipAnimationToEnd); - TestUtils.waitForCondition( - () -> !animator.isStarted(), "Animation did not end."); + TestUtils.waitForCondition(mAnimationStopped::get, "Animation did not stop."); } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 10110e219d21..f97d6b3d262f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene +import android.content.Context import android.content.pm.UserInfo import android.graphics.Bitmap import android.graphics.drawable.BitmapDrawable @@ -81,8 +82,10 @@ import kotlinx.coroutines.test.currentTime */ @OptIn(ExperimentalCoroutinesApi::class) class SceneTestUtils( - test: SysuiTestCase, + private val context: Context, ) { + constructor(test: SysuiTestCase) : this(context = test.context) + val kosmos = Kosmos() val testDispatcher = kosmos.testDispatcher val testScope = kosmos.testScope @@ -115,8 +118,6 @@ class SceneTestUtils( } } - private val context = test.context - private val falsingCollectorFake: FalsingCollector by lazy { FalsingCollectorFake() } private var falsingInteractor: FalsingInteractor? = null diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java index 216f45acd5bd..d722f2f1aa0c 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java @@ -279,6 +279,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection, mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection, Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY | Context.BIND_SCHEDULE_LIKE_TOP_APP + | Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser)); } diff --git a/tools/fonts/update_font_metadata.py b/tools/fonts/update_font_metadata.py index c07a98a1e3d2..04a552886d42 100755 --- a/tools/fonts/update_font_metadata.py +++ b/tools/fonts/update_font_metadata.py @@ -19,7 +19,7 @@ def main(): args_parser.add_argument('--revision', help='Updated font revision. Use + to update revision based on the current revision') args = args_parser.parse_args() - font = ttLib.TTFont(args.input) + font = ttLib.TTFont(args.input, recalcTimestamp=False) update_font_revision(font, args.revision) font.save(args.output) |