summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2025-02-20 14:52:48 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-20 14:52:48 -0800
commitca8ecbebc7e95f3dfec071fc68f43f0d388f7b7c (patch)
treea8f9f5746c1e6c19ec3b8fc04e33d510a12f4d5c
parent0040fe7b5229a7858d09ccc3856799696ea9114d (diff)
parent4d3dfb14937619c260a1adfce4bf6db7e90a7935 (diff)
Merge changes from topic "minimize_cuj_in_animator" into main
* changes: Use animHandler for CUJ_DESKTOP_MODE_CLOSE_TASK Desktop-Minimize: move CUJ handling to MinimizeAnimator
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt50
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java48
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt8
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt82
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt118
11 files changed, 260 insertions, 162 deletions
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
index e92c1eb81e89..43dd9b73b352 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/Interpolators.java
@@ -74,6 +74,12 @@ public class Interpolators {
0.05f, 0.7f, 0.1f, 1f);
/**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ public static final Interpolator STANDARD_ACCELERATE = new PathInterpolator(0.3f, 0f, 1f, 1f);
+
+ /**
* The standard decelerating interpolator that should be used on every regular movement of
* content that is appearing e.g. when coming from off screen.
*/
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
index 0586e265eced..4ecace0292cf 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/animation/MinimizeAnimator.kt
@@ -19,52 +19,76 @@ package com.android.wm.shell.shared.animation
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
-import android.util.DisplayMetrics
+import android.content.Context
+import android.os.Handler
+import android.view.Choreographer
import android.view.SurfaceControl.Transaction
-import android.view.animation.LinearInterpolator
-import android.view.animation.PathInterpolator
import android.window.TransitionInfo.Change
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
/** Creates minimization animation */
object MinimizeAnimator {
private const val MINIMIZE_ANIM_ALPHA_DURATION_MS = 100L
- private val STANDARD_ACCELERATE = PathInterpolator(0.3f, 0f, 1f, 1f)
-
private val minimizeBoundsAnimationDef =
WindowAnimator.BoundsAnimationParams(
durationMs = 200,
endOffsetYDp = 12f,
endScale = 0.97f,
- interpolator = STANDARD_ACCELERATE,
+ interpolator = Interpolators.STANDARD_ACCELERATE,
)
+ /**
+ * Creates a minimize animator for given task [Change].
+ *
+ * @param onAnimFinish finish-callback for the animation, note that this is called on the same
+ * thread as the animation itself.
+ * @param animationHandler the Handler that the animation is running on.
+ */
@JvmStatic
fun create(
- displayMetrics: DisplayMetrics,
+ context: Context,
change: Change,
transaction: Transaction,
onAnimFinish: (Animator) -> Unit,
+ interactionJankMonitor: InteractionJankMonitor,
+ animationHandler: Handler,
): Animator {
val boundsAnimator = WindowAnimator.createBoundsAnimator(
- displayMetrics,
+ context.resources.displayMetrics,
minimizeBoundsAnimationDef,
change,
transaction,
)
val alphaAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
duration = MINIMIZE_ANIM_ALPHA_DURATION_MS
- interpolator = LinearInterpolator()
+ interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
- transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
+ transaction
+ .setAlpha(change.leash, animation.animatedValue as Float)
+ .setFrameTimeline(Choreographer.getInstance().vsyncId)
+ .apply()
}
}
val listener = object : Animator.AnimatorListener {
- override fun onAnimationEnd(animator: Animator) = onAnimFinish(animator)
- override fun onAnimationCancel(animator: Animator) = Unit
+ override fun onAnimationStart(animator: Animator) {
+ interactionJankMonitor.begin(
+ change.leash,
+ context,
+ animationHandler,
+ CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
+ )
+ }
+ override fun onAnimationCancel(animator: Animator) {
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
override fun onAnimationRepeat(animator: Animator) = Unit
- override fun onAnimationStart(animator: Animator) = Unit
+ override fun onAnimationEnd(animator: Animator) {
+ interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ onAnimFinish(animator)
+ }
}
return AnimatorSet().apply {
playTogether(boundsAnimator, alphaAnimator)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
index ee3e39e71558..e9dc6132f5f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java
@@ -162,22 +162,31 @@ public abstract class WMShellConcurrencyModule {
}
}
- /**
- * Provide a Shell animation-thread Executor.
- */
+ /** Provide a Shell animation-thread Handler. */
@WMSingleton
@Provides
@ShellAnimationThread
- public static ShellExecutor provideShellAnimationExecutor() {
- HandlerThread shellAnimationThread = new HandlerThread("wmshell.anim",
- THREAD_PRIORITY_DISPLAY);
- shellAnimationThread.start();
+ public static Handler provideShellAnimationHandler() {
+ HandlerThread animThread = new HandlerThread("wmshell.anim", THREAD_PRIORITY_DISPLAY);
+ animThread.start();
if (Build.IS_DEBUGGABLE) {
- shellAnimationThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
- shellAnimationThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
+ animThread.getLooper().setTraceTag(Trace.TRACE_TAG_WINDOW_MANAGER);
+ animThread.getLooper().setSlowLogThresholdMs(MSGQ_SLOW_DISPATCH_THRESHOLD_MS,
MSGQ_SLOW_DELIVERY_THRESHOLD_MS);
}
- return new HandlerExecutor(Handler.createAsync(shellAnimationThread.getLooper()));
+ return Handler.createAsync(animThread.getLooper());
+ }
+
+ /**
+ * Provide a Shell animation-thread Executor.
+ */
+ @WMSingleton
+ @Provides
+ @ShellAnimationThread
+ public static ShellExecutor provideShellAnimationExecutor(
+ @ShellAnimationThread Handler animHandler
+ ) {
+ return new HandlerExecutor(animHandler);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 5a246e7c99b9..5d5e4d3ec758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -431,9 +431,10 @@ public abstract class WMShellModule {
Transitions transitions,
DisplayController displayController,
@ShellMainThread ShellExecutor mainExecutor,
- @ShellAnimationThread ShellExecutor animExecutor) {
+ @ShellAnimationThread ShellExecutor animExecutor,
+ @ShellAnimationThread Handler animHandler) {
return new FreeformTaskTransitionHandler(
- transitions, displayController, mainExecutor, animExecutor);
+ transitions, displayController, mainExecutor, animExecutor, animHandler);
}
@WMSingleton
@@ -1101,8 +1102,9 @@ public abstract class WMShellModule {
Context context,
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
- @ShellMainThread Handler handler) {
- return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor, handler);
+ @ShellAnimationThread Handler animHandler) {
+ return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor,
+ animHandler);
}
@WMSingleton
@@ -1110,9 +1112,10 @@ public abstract class WMShellModule {
static DesktopMinimizationTransitionHandler provideDesktopMinimizationTransitionHandler(
@ShellMainThread ShellExecutor mainExecutor,
@ShellAnimationThread ShellExecutor animExecutor,
- DisplayController displayController) {
+ DisplayController displayController,
+ @ShellAnimationThread Handler mainHandler) {
return new DesktopMinimizationTransitionHandler(mainExecutor, animExecutor,
- displayController);
+ displayController, mainHandler);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
index 1ce093e02a4a..b22a46edbf3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandler.kt
@@ -37,7 +37,6 @@ import com.android.app.animation.Interpolators
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_CLOSE_TASK
import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.common.ShellExecutor
-import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.transition.Transitions
import java.util.function.Supplier
@@ -49,7 +48,7 @@ constructor(
private val mainExecutor: ShellExecutor,
private val animExecutor: ShellExecutor,
private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
- @ShellMainThread private val handler: Handler,
+ private val animHandler: Handler,
) : Transitions.TransitionHandler {
private val runningAnimations = mutableMapOf<IBinder, List<Animator>>()
@@ -95,7 +94,7 @@ constructor(
interactionJankMonitor.begin(
lastChangeLeash,
context,
- handler,
+ animHandler,
CUJ_DESKTOP_MODE_CLOSE_TASK,
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
index 728638d9bbd3..7074e8bc9cce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandler.kt
@@ -18,14 +18,17 @@ package com.android.wm.shell.desktopmode
import android.animation.Animator
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.os.Handler
import android.os.IBinder
-import android.util.DisplayMetrics
import android.view.SurfaceControl.Transaction
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.animation.MinimizeAnimator.create
import com.android.wm.shell.transition.Transitions
@@ -41,6 +44,7 @@ class DesktopMinimizationTransitionHandler(
private val mainExecutor: ShellExecutor,
private val animExecutor: ShellExecutor,
private val displayController: DisplayController,
+ private val animHandler: Handler,
) : Transitions.TransitionHandler {
/** Shouldn't handle anything */
@@ -90,10 +94,30 @@ class DesktopMinimizationTransitionHandler(
val t = Transaction()
val sc = change.leash
finishTransaction.hide(sc)
- val displayMetrics: DisplayMetrics? =
- change.taskInfo?.let {
- displayController.getDisplayContext(it.displayId)?.getResources()?.displayMetrics
- }
- return displayMetrics?.let { create(it, change, t, onAnimFinish) }
+ val displayContext =
+ change.taskInfo?.let { displayController.getDisplayContext(it.displayId) }
+ if (displayContext == null) {
+ logW(
+ "displayContext is null for taskId=${change.taskInfo?.taskId}, " +
+ "displayId=${change.taskInfo?.displayId}"
+ )
+ return null
+ }
+ return create(
+ displayContext,
+ change,
+ t,
+ onAnimFinish,
+ InteractionJankMonitor.getInstance(),
+ animHandler,
+ )
+ }
+
+ private companion object {
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
+ const val TAG = "DesktopMinimizationTransitionHandler"
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
index 81b136dd1569..f9ab359e952d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt
@@ -26,7 +26,6 @@ import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import androidx.annotation.VisibleForTesting
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
@@ -176,28 +175,13 @@ class DesktopTasksLimiter(
return taskChange.mode == TRANSIT_TO_BACK
}
- override fun onTransitionStarting(transition: IBinder) {
- val mActiveTaskDetails = activeTransitionTokensAndTasks[transition]
- val info = mActiveTaskDetails?.transitionInfo ?: return
- val minimizeChange = getMinimizeChange(info, mActiveTaskDetails.taskId) ?: return
- // Begin minimize window CUJ instrumentation.
- interactionJankMonitor.begin(
- minimizeChange.leash,
- context,
- handler,
- CUJ_DESKTOP_MODE_MINIMIZE_WINDOW,
- )
- }
-
private fun getMinimizeChange(info: TransitionInfo, taskId: Int): TransitionInfo.Change? =
info.changes.find { change ->
change.taskInfo?.taskId == taskId && change.mode == TRANSIT_TO_BACK
}
override fun onTransitionMerged(merged: IBinder, playing: IBinder) {
- if (activeTransitionTokensAndTasks.remove(merged) != null) {
- interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- }
+ activeTransitionTokensAndTasks.remove(merged)
pendingTransitionTokensAndTasks.remove(merged)?.let { taskToTransfer ->
pendingTransitionTokensAndTasks[playing] = taskToTransfer
}
@@ -209,13 +193,6 @@ class DesktopTasksLimiter(
}
override fun onTransitionFinished(transition: IBinder, aborted: Boolean) {
- if (activeTransitionTokensAndTasks.remove(transition) != null) {
- if (aborted) {
- interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- } else {
- interactionJankMonitor.end(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
- }
- }
pendingTransitionTokensAndTasks.remove(transition)
activeUnminimizeTransitionTokensAndTasks.remove(transition)
pendingUnminimizeTransitionTokensAndTasks.remove(transition)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
index b60fb5e7bfdd..16e411e1fc07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionHandler.java
@@ -25,10 +25,12 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Rect;
+import android.os.Handler;
import android.os.IBinder;
import android.util.ArrayMap;
-import android.util.DisplayMetrics;
+import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -38,6 +40,7 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.jank.InteractionJankMonitor;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.animation.MinimizeAnimator;
@@ -52,11 +55,13 @@ import java.util.List;
*/
public class FreeformTaskTransitionHandler
implements Transitions.TransitionHandler, FreeformTaskTransitionStarter {
+ private static final String TAG = "FreeformTaskTransitionHandler";
private static final int CLOSE_ANIM_DURATION = 400;
private final Transitions mTransitions;
private final DisplayController mDisplayController;
private final ShellExecutor mMainExecutor;
private final ShellExecutor mAnimExecutor;
+ private final Handler mAnimHandler;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -66,11 +71,13 @@ public class FreeformTaskTransitionHandler
Transitions transitions,
DisplayController displayController,
ShellExecutor mainExecutor,
- ShellExecutor animExecutor) {
+ ShellExecutor animExecutor,
+ Handler animHandler) {
mTransitions = transitions;
mDisplayController = displayController;
mMainExecutor = mainExecutor;
mAnimExecutor = animExecutor;
+ mAnimHandler = animHandler;
}
@Override
@@ -123,13 +130,11 @@ public class FreeformTaskTransitionHandler
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean transitionHandled = false;
final ArrayList<Animator> animations = new ArrayList<>();
- final Runnable onAnimFinish = () -> {
+ final Runnable onAnimFinish = () -> mMainExecutor.execute(() -> {
if (!animations.isEmpty()) return;
- mMainExecutor.execute(() -> {
- mAnimations.remove(transition);
- finishCallback.onTransitionFinished(null /* wct */);
- });
- };
+ mAnimations.remove(transition);
+ finishCallback.onTransitionFinished(null /* wct */);
+ });
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
@@ -234,18 +239,25 @@ public class FreeformTaskTransitionHandler
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
SurfaceControl sc = change.getLeash();
finishT.hide(sc);
- final DisplayMetrics displayMetrics =
- mDisplayController
- .getDisplayContext(taskInfo.displayId).getResources().getDisplayMetrics();
+ final Context displayContext =
+ mDisplayController.getDisplayContext(taskInfo.displayId);
+ if (displayContext == null) {
+ Log.w(TAG, "No displayContext for displayId=" + taskInfo.displayId);
+ return false;
+ }
final Animator animator = MinimizeAnimator.create(
- displayMetrics,
+ displayContext,
change,
t,
(anim) -> {
- animations.remove(anim);
- onAnimFinish.run();
+ mMainExecutor.execute(() -> {
+ animations.remove(anim);
+ onAnimFinish.run();
+ });
return null;
- });
+ },
+ InteractionJankMonitor.getInstance(),
+ mAnimHandler);
animations.add(animator);
return true;
}
@@ -277,8 +289,10 @@ public class FreeformTaskTransitionHandler
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- animations.remove(animator);
- onAnimFinish.run();
+ mMainExecutor.execute(() -> {
+ animations.remove(animator);
+ onAnimFinish.run();
+ });
}
});
animations.add(animator);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
index 4c3325d4d1de..0d1c57221fb9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMinimizationTransitionHandlerTest.kt
@@ -21,6 +21,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WindowingMode
+import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
@@ -56,7 +57,12 @@ class DesktopMinimizationTransitionHandlerTest : ShellTestCase() {
@Before
fun setUp() {
handler =
- DesktopMinimizationTransitionHandler(testExecutor, testExecutor, displayController)
+ DesktopMinimizationTransitionHandler(
+ testExecutor,
+ testExecutor,
+ displayController,
+ mock<Handler>(),
+ )
whenever(displayController.getDisplayContext(any())).thenReturn(mContext)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index d33209dbc30e..62e3c544e390 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -37,7 +37,6 @@ import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.StaticMockitoSession
-import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
import com.android.internal.jank.InteractionJankMonitor
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
import com.android.wm.shell.ShellTaskOrganizer
@@ -72,8 +71,6 @@ import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.`when`
-import org.mockito.kotlin.eq
-import org.mockito.kotlin.verify
import org.mockito.quality.Strictness
/**
@@ -521,85 +518,6 @@ class DesktopTasksLimiterTest : ShellTestCase() {
}
@Test
- fun minimizeTransitionReadyAndFinished_logsJankInstrumentationBeginAndEnd() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val transition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(transition, taskId = task.taskId)
-
- callOnTransitionReady(
- transition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionFinished(transition, /* aborted= */ false)
-
- verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
- fun minimizeTransitionReadyAndAborted_logsJankInstrumentationBeginAndCancel() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val transition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(transition, taskId = task.taskId)
-
- callOnTransitionReady(
- transition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionFinished(transition, /* aborted= */ true)
-
- verify(interactionJankMonitor).cancel(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
- fun minimizeTransitionReadyAndMerged_logsJankInstrumentationBeginAndEnd() {
- desktopTaskRepo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- desktopTaskRepo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 0)
- (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() }
- val mergedTransition = Binder()
- val newTransition = Binder()
- val task = setUpFreeformTask()
- addPendingMinimizeChange(mergedTransition, taskId = task.taskId)
-
- callOnTransitionReady(
- mergedTransition,
- TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
- )
-
- desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
-
- verify(interactionJankMonitor)
- .begin(any(), eq(mContext), eq(handler), eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
-
- desktopTasksLimiter
- .getTransitionObserver()
- .onTransitionMerged(mergedTransition, newTransition)
-
- verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW))
- }
-
- @Test
fun getMinimizingTask_noPendingTransition_returnsNull() {
val transition = Binder()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt
new file mode 100644
index 000000000000..ba609d4322fc
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/animation/MinimizeAnimatorTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2025 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.wm.shell.shared.animation
+
+import android.animation.Animator
+import android.animation.AnimatorSet
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Resources
+import android.os.Handler
+import android.util.DisplayMetrics
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.window.TransitionInfo
+import android.window.WindowContainerToken
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW
+import com.android.internal.jank.InteractionJankMonitor
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MinimizeAnimatorTest {
+ private val context = mock<Context>()
+ private val resources = mock<Resources>()
+ private val transaction = mock<Transaction>()
+ private val leash = mock<SurfaceControl>()
+ private val interactionJankMonitor = mock<InteractionJankMonitor>()
+ private val animationHandler = mock<Handler>()
+
+ private val displayMetrics = DisplayMetrics().apply { density = 1f }
+
+ @Before
+ fun setup() {
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.displayMetrics).thenReturn(displayMetrics)
+ whenever(transaction.setAlpha(any(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), anyFloat(), anyFloat())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(anyLong())).thenReturn(transaction)
+ }
+
+ @Test
+ fun create_returnsBoundsAndAlphaAnimators() {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ val animator = createAnimator(change)
+
+ assertThat(animator).isInstanceOf(AnimatorSet::class.java)
+ val animatorSet = animator as AnimatorSet
+ assertThat(animatorSet.childAnimations).hasSize(2)
+ assertIsBoundsAnimator(animatorSet.childAnimations[0])
+ assertIsAlphaAnimator(animatorSet.childAnimations[1])
+ }
+
+ @Test
+ fun create_doesNotlogJankInstrumentation() = runOnUiThread {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ createAnimator(change)
+
+ verify(interactionJankMonitor, never()).begin(
+ leash, context, animationHandler, CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
+
+ @Test
+ fun onAnimationStart_logsJankInstrumentation() = runOnUiThread {
+ val change = TransitionInfo.Change(mock<WindowContainerToken>(), leash)
+
+ createAnimator(change).start()
+
+ verify(interactionJankMonitor).begin(
+ leash, context, animationHandler, CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)
+ }
+
+ private fun createAnimator(change: TransitionInfo.Change): Animator =
+ MinimizeAnimator.create(context, change, transaction, {}, interactionJankMonitor,
+ animationHandler)
+
+ private fun assertIsBoundsAnimator(animator: Animator) {
+ assertThat(animator).isInstanceOf(ValueAnimator::class.java)
+ assertThat(animator.duration).isEqualTo(200)
+ assertThat(animator.interpolator).isEqualTo(Interpolators.STANDARD_ACCELERATE)
+ }
+
+ private fun assertIsAlphaAnimator(animator: Animator) {
+ assertThat(animator).isInstanceOf(ValueAnimator::class.java)
+ assertThat(animator.duration).isEqualTo(100)
+ assertThat(animator.interpolator).isEqualTo(Interpolators.LINEAR)
+ }
+}
+