Add end animation listener for trampoline animation
Bug: 399322301
Flag: com.android.window.flags.enable_desktop_trampoline_close_animation_bugfix
Test: DesktopAppLaunchAnimatorHelperTest
Change-Id: I2d1aaf58113f17b4aa172268e4e9e5fba87cbbd3
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
index 1438edf..a9e5145 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopAppLaunchAnimatorHelper.kt
@@ -93,7 +93,7 @@
}
if (trampolineCloseChange != null) {
val trampolineCloseAnimator =
- createTrampolineCloseAnimator(trampolineCloseChange, transaction)
+ createTrampolineCloseAnimator(trampolineCloseChange, transaction, finishCallback)
animatorsList.add(trampolineCloseAnimator)
}
return animatorsList
@@ -112,7 +112,7 @@
private fun getTrampolineCloseChange(info: TransitionInfo): Change? {
if (
info.changes.size < 2 ||
- !DesktopModeFlags.ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX.isTrue
+ !DesktopModeFlags.ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX.isTrue
) {
return null
}
@@ -194,13 +194,22 @@
)
}
- private fun createTrampolineCloseAnimator(change: Change, transaction: Transaction): Animator {
+ private fun createTrampolineCloseAnimator(
+ change: Change,
+ transaction: Transaction,
+ onAnimFinish: (Animator) -> Unit,
+ ): Animator {
return ValueAnimator.ofFloat(1f, 0f).apply {
duration = 100L
interpolator = Interpolators.LINEAR
addUpdateListener { animation ->
transaction.setAlpha(change.leash, animation.animatedValue as Float).apply()
}
+ addListener(
+ onEnd = { animation ->
+ onAnimFinish(animation)
+ }
+ )
}
}
diff --git a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
index 47108e0..daa77d2 100644
--- a/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/desktop/DesktopAppLaunchAnimatorHelperTest.kt
@@ -32,7 +32,9 @@
import android.view.SurfaceControl
import android.view.WindowManager
import android.window.TransitionInfo
+import android.window.TransitionInfo.Change
import androidx.core.util.Supplier
+import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import com.android.app.animation.Interpolators
import com.android.internal.jank.Cuj
import com.android.launcher3.desktop.DesktopAppLaunchAnimatorHelper
@@ -45,6 +47,7 @@
import org.junit.Test
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
class DesktopAppLaunchAnimatorHelperTest {
@@ -70,6 +73,10 @@
whenever(transactionSupplier.get()).thenReturn(transaction)
whenever(transaction.setCrop(any(), any())).thenReturn(transaction)
whenever(transaction.setCornerRadius(any(), any())).thenReturn(transaction)
+ whenever(transaction.setScale(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setPosition(any(), any(), any())).thenReturn(transaction)
+ whenever(transaction.setAlpha(any(), any())).thenReturn(transaction)
+ whenever(transaction.setFrameTimeline(any())).thenReturn(transaction)
whenever(context.resources).thenReturn(resources)
whenever(resources.displayMetrics).thenReturn(DisplayMetrics())
@@ -77,14 +84,8 @@
}
@Test
- fun launchTransition_returnsLaunchAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
+ fun launchTransition_returnsLaunchAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -93,14 +94,27 @@
}
@Test
- fun noLaunchTransition_returnsEmptyAnimatorsList() {
+ fun launchTransition_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
+ fun noLaunchTransition_returnsEmptyAnimatorsList() = runOnUiThread {
val pipChange =
TransitionInfo.Change(mock(), mock()).apply {
mode = WindowManager.TRANSIT_PIP
taskInfo = TASK_INFO_FREEFORM
}
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(pipChange)
+ val transitionInfo = createTransitionInfo(listOf(pipChange))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -108,20 +122,8 @@
}
@Test
- fun minimizeTransition_returnsLaunchAndMinimizeAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
+ fun minimizeTransition_returnsLaunchAndMinimizeAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, MINIMIZE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -131,21 +133,23 @@
}
@Test
+ fun minimizeTransition_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, MINIMIZE_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagEnabled_returnsLaunchAndCloseAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagEnabled_returnsLaunchAndCloseAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -155,21 +159,24 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
+ fun trampolineTransition_flagEnabled_callsAnimationEndListener() = runOnUiThread {
+ val finishCallback = mock<Function1<Animator, Unit>>()
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
+
+ val animators = helper.createAnimators(transitionInfo, finishCallback = finishCallback)
+
+ animators.forEach { animator ->
+ animator.start()
+ animator.end()
+ verify(finishCallback).invoke(animator)
+ }
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagDisabled_returnsLaunchAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagDisabled_returnsLaunchAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(listOf(OPEN_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -179,26 +186,9 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagEnabled_hitDesktopWindowLimit_returnsLaunchMinimizeCloseAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagEnabled_hitDesktopWindowLimit_returnsLaunchMinimizeCloseAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(
+ listOf(OPEN_CHANGE, MINIMIZE_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -210,26 +200,9 @@
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX)
- fun trampolineTransition_flagDisabled_hitDesktopWindowLimit_returnsLaunchMinimizeAnimator() {
- val openChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_OPEN
- taskInfo = TASK_INFO_FREEFORM
- }
- val minimizeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_TO_BACK
- taskInfo = TASK_INFO_FREEFORM
- }
- val closeChange =
- TransitionInfo.Change(mock(), mock()).apply {
- mode = WindowManager.TRANSIT_CLOSE
- taskInfo = TASK_INFO_FREEFORM
- }
- val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
- transitionInfo.addChange(openChange)
- transitionInfo.addChange(minimizeChange)
- transitionInfo.addChange(closeChange)
+ fun trampolineTransition_flagDisabled_hitDesktopWindowLimit_returnsLaunchMinimizeAnimator() = runOnUiThread {
+ val transitionInfo = createTransitionInfo(
+ listOf(OPEN_CHANGE, MINIMIZE_CHANGE, CLOSE_CHANGE))
val actual = helper.createAnimators(transitionInfo, finishCallback = {})
@@ -280,6 +253,12 @@
assertThat(animator.duration).isEqualTo(100)
}
+ private fun createTransitionInfo(changes: List<Change>): TransitionInfo {
+ val transitionInfo = TransitionInfo(WindowManager.TRANSIT_NONE, 0)
+ changes.forEach { transitionInfo.addChange(it) }
+ return transitionInfo
+ }
+
private companion object {
val TASK_INFO_FREEFORM =
ActivityManager.RunningTaskInfo().apply {
@@ -290,5 +269,23 @@
configuration.windowConfiguration.windowingMode =
WindowConfiguration.WINDOWING_MODE_FREEFORM
}
+
+ val OPEN_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_OPEN
+ taskInfo = TASK_INFO_FREEFORM
+ }
+
+ val CLOSE_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_CLOSE
+ taskInfo = TASK_INFO_FREEFORM
+ }
+
+ val MINIMIZE_CHANGE =
+ TransitionInfo.Change(mock(), mock()).apply {
+ mode = WindowManager.TRANSIT_TO_BACK
+ taskInfo = TASK_INFO_FREEFORM
+ }
}
}