Merge "Extend USB compliance warning API" into main
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 87324db..58ee2b2 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.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 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 @@
ACTION_SMARTSPACE_DOORBELL,
ACTION_NOTIFICATION_BIG_PICTURE_LOADED,
ACTION_KEYGUARD_FPS_UNLOCK_TO_HOME,
+ ACTION_BACK_SYSTEM_ANIMATION,
};
/** @hide */
@@ -283,6 +290,7 @@
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 @@
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 @@
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 03c546d..5843635 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 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 @@
* 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 @@
private final BackAnimationBackground mAnimationBackground;
private StatusBarCustomizer mCustomizer;
+ private boolean mTrackingLatency;
public BackAnimationController(
@NonNull ShellInit shellInit,
@@ -213,6 +216,7 @@
.setSpeedUpFactor(FLING_SPEED_UP_FACTOR)
.build();
mShellBackAnimationRegistry = shellBackAnimationRegistry;
+ mLatencyTracker = LatencyTracker.getInstance(mContext);
}
private void onInit() {
@@ -438,6 +442,7 @@
private void startBackNavigation(@NonNull TouchTracker touchTracker) {
try {
+ startLatencyTracking();
mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
mNavigationObserver, mEnableAnimations.get() ? mBackAnimationAdapter : null);
onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
@@ -452,6 +457,7 @@
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 @@
}
} else {
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
+ // App is handling back animation. Cancel system animation latency tracking.
+ cancelLatencyTracking();
dispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
}
}
@@ -808,12 +816,36 @@
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 @@
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 1941d66..652a2ed 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 @@
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 @@
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 @@
}
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 531de1f..8c5d4a2 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.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 @@
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
+ // 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_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
+ 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 @@
fun animateOpen() {
prepareMenuForAnimation()
appInfoPillExpand()
- animateAppInfoPill()
- animateWindowingPill()
- animateMoreActionsPill()
+ animateAppInfoPillOpen()
+ animateWindowingPillOpen()
+ animateMoreActionsPillOpen()
runAnimations()
}
@@ -81,13 +91,44 @@
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 @@
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 @@
// 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 @@
// 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 0000000..de46f72
--- /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 72935f7..d92b506 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 @@
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 761551c..34d7cec 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 @@
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 acad36e..4aa0d89 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 @@
@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 e3a2c59..d77a80a 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.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 @@
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 10110e2..f97d6b3 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 @@
*/
@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 @@
}
}
- 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 216f45a..d722f2f 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 @@
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 c07a98a..04a5528 100755
--- a/tools/fonts/update_font_metadata.py
+++ b/tools/fonts/update_font_metadata.py
@@ -19,7 +19,7 @@
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)