Merge "Wallpaper animation for keyguard unlocking." into udc-dev

This commit is contained in:
Marzia Favaro
2023-05-10 15:09:12 +00:00
committed by Android (Google) Code Review
3 changed files with 172 additions and 17 deletions

View File

@@ -245,6 +245,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
@VisibleForTesting
var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null
private var wallpaperTargets: Array<RemoteAnimationTarget>? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
/**
@@ -257,9 +258,13 @@ class KeyguardUnlockAnimationController @Inject constructor(
*/
private var surfaceBehindAlpha = 1f
private var wallpaperAlpha = 1f
@VisibleForTesting
var surfaceBehindAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
var wallpaperAlphaAnimator = ValueAnimator.ofFloat(0f, 1f)
/**
* Matrix applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
* app/launcher behind the keyguard.
@@ -335,6 +340,27 @@ class KeyguardUnlockAnimationController @Inject constructor(
})
}
with(wallpaperAlphaAnimator) {
duration = LAUNCHER_ICONS_ANIMATION_DURATION_MS
interpolator = Interpolators.ALPHA_OUT
addUpdateListener { valueAnimator: ValueAnimator ->
wallpaperAlpha = valueAnimator.animatedValue as Float
setWallpaperAppearAmount(wallpaperAlpha)
}
addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
Log.d(TAG, "wallpaperAlphaAnimator#onAnimationEnd, animation ended ")
if (wallpaperAlpha == 1f) {
wallpaperTargets = null
keyguardViewMediator.get().finishExitRemoteAnimation()
} else {
Log.d(TAG, "wallpaperAlphaAnimator#onAnimationEnd, " +
"animation was cancelled: skipping finishAnimation()")
}
}
})
}
with(surfaceBehindEntryAnimator) {
duration = UNLOCK_ANIMATION_DURATION_MS
startDelay = UNLOCK_ANIMATION_SURFACE_BEHIND_START_DELAY_MS
@@ -361,6 +387,11 @@ class KeyguardUnlockAnimationController @Inject constructor(
context.resources.getDimensionPixelSize(R.dimen.rounded_corner_radius).toFloat()
}
fun isAnyKeyguyardAnimatorPlaying(): Boolean {
return surfaceBehindAlphaAnimator.isStarted ||
wallpaperAlphaAnimator.isStarted || surfaceBehindEntryAnimator.isStarted
}
/**
* Add a listener to be notified of various stages of the unlock animation.
*/
@@ -492,6 +523,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
*/
fun notifyStartSurfaceBehindRemoteAnimation(
targets: Array<RemoteAnimationTarget>,
wallpapers: Array<RemoteAnimationTarget>,
startTime: Long,
requestedShowSurfaceBehindKeyguard: Boolean
) {
@@ -501,8 +533,11 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
surfaceBehindRemoteAnimationTargets = targets
wallpaperTargets = wallpapers
surfaceBehindRemoteAnimationStartTime = startTime
fadeInWallpaper()
// If we specifically requested that the surface behind be made visible (vs. it being made
// visible because we're unlocking), then we're in the middle of a swipe-to-unlock touch
// gesture and the surface behind the keyguard should be made visible so that we can animate
@@ -838,6 +873,38 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
}
/**
* Modify the opacity of a wallpaper window.
*/
fun setWallpaperAppearAmount(amount: Float) {
wallpaperTargets?.forEach { wallpaper ->
val animationAlpha = when {
// If the screen has turned back off, the unlock animation is going to be cancelled,
// so set the surface alpha to 0f so it's no longer visible.
!powerManager.isInteractive -> 0f
else -> amount
}
// SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
// unable to draw
val sc: SurfaceControl? = wallpaper.leash
if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
sc?.isValid == true) {
with(SurfaceControl.Transaction()) {
setAlpha(sc, animationAlpha)
apply()
}
} else {
applyParamsToSurface(
SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
wallpaper.leash)
.withAlpha(animationAlpha)
.build()
)
}
}
}
/**
* Called by [KeyguardViewMediator] to let us know that the remote animation has finished, and
* we should clean up all of our state.
@@ -903,6 +970,12 @@ class KeyguardUnlockAnimationController @Inject constructor(
surfaceBehindAlphaAnimator.start()
}
private fun fadeInWallpaper() {
Log.d(TAG, "fadeInWallpaper")
wallpaperAlphaAnimator.cancel()
wallpaperAlphaAnimator.start()
}
private fun fadeOutSurfaceBehind() {
Log.d(TAG, "fadeOutSurfaceBehind")
surfaceBehindAlphaAnimator.cancel()

View File

@@ -159,6 +159,7 @@ import dagger.Lazy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.Executor;
/**
@@ -2710,9 +2711,13 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel"));
// Pass the surface and metadata to the unlock animation controller.
RemoteAnimationTarget[] openingWallpapers = Arrays.stream(wallpapers).filter(
w -> w.mode == RemoteAnimationTarget.MODE_OPENING).toArray(
RemoteAnimationTarget[]::new);
mKeyguardUnlockAnimationControllerLazy.get()
.notifyStartSurfaceBehindRemoteAnimation(
apps, startTime, mSurfaceBehindRemoteAnimationRequested);
apps, openingWallpapers, startTime,
mSurfaceBehindRemoteAnimationRequested);
} else {
mInteractionJankMonitor.begin(
createInteractionJankMonitorConf(
@@ -2961,6 +2966,19 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
mSurfaceBehindRemoteAnimationRequested = false;
mSurfaceBehindRemoteAnimationRunning = false;
mKeyguardStateController.notifyKeyguardGoingAway(false);
finishExitRemoteAnimation();
}
void finishExitRemoteAnimation() {
if (mKeyguardUnlockAnimationControllerLazy.get().isAnyKeyguyardAnimatorPlaying()
|| mKeyguardStateController.isDismissingFromSwipe()) {
// If the animation is ongoing, or we are not done with the swipe gesture,
// it's too early to terminate the animation
Log.d(TAG, "finishAnimation not executing now because "
+ "not all animations have finished");
return;
}
Log.d(TAG, "finishAnimation executing");
if (mSurfaceBehindRemoteAnimationFinishedCallback != null) {
try {

View File

@@ -20,6 +20,7 @@ import com.android.systemui.statusbar.NotificationShadeWindowController
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.whenever
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertFalse
@@ -28,13 +29,14 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor.forClass
import org.mockito.Mock
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
import java.util.function.Predicate
@RunWith(AndroidTestingRunner::class)
@RunWithLooper
@@ -77,6 +79,13 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
mock(ActivityManager.RunningTaskInfo::class.java), false)
private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
private var surfaceControlWp = mock(SurfaceControl::class.java)
private var wallpaperTarget = RemoteAnimationTarget(
2 /* taskId */, 0, surfaceControlWp, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
mock(WindowConfiguration::class.java), false, surfaceControlWp, Rect(),
mock(ActivityManager.RunningTaskInfo::class.java), false)
private lateinit var wallpaperTargets: Array<RemoteAnimationTarget>
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -94,6 +103,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
// All of these fields are final, so we can't mock them, but are needed so that the surface
// appear amount setter doesn't short circuit.
remoteAnimationTargets = arrayOf(remoteTarget1)
wallpaperTargets = arrayOf(wallpaperTarget)
// Set the surface applier to our mock so that we can verify the arguments passed to it.
// This applier does not have any side effects within the unlock animation controller, so
@@ -119,18 +129,20 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
arrayOf(),
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, times(1)).scheduleApply(
captorSb.capture { sp -> sp.surface == surfaceControl1 })
val params = captor.value
val params = captorSb.getLastValue()
// We expect that we've instantly set the surface behind to alpha = 1f, and have no
// transforms (translate, scale) on its matrix.
assertEquals(params.alpha, 1f)
assertEquals(1f, params.alpha)
assertTrue(params.matrix.isIdentity)
// Also expect we've immediately asked the keyguard view mediator to finish the remote
@@ -150,6 +162,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -174,6 +187,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -196,6 +210,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -216,6 +231,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -230,6 +246,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -245,6 +262,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -259,6 +277,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
fun surfaceAnimation_multipleTargets() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
arrayOf(remoteTarget1, remoteTarget2),
wallpaperTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -267,10 +286,15 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
// means an animation is in progress.
keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
verify(surfaceTransactionApplier, times(2)).scheduleApply(captor.capture())
val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, times(2)).scheduleApply(captorSb
.capture { sp -> sp.surface == surfaceControl1 || sp.surface == surfaceControl2 })
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, times(1).description(
"WallpaperSurface was expected to receive scheduleApply once"
)).scheduleApply(captorWp.capture { sp -> sp.surface == surfaceControlWp})
val allParams = captor.allValues
val allParams = captorSb.getAllValues()
val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2)
allParams.forEach { params ->
@@ -293,20 +317,29 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, times(1)).scheduleApply(
captorSb.capture { sp -> sp.surface == surfaceControl1})
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " +
"received scheduleApply")).scheduleApply(
captorWp.capture { sp -> sp.surface == surfaceControlWp })
val params = captor.value
val params = captorSb.getLastValue()
// We expect that we've set the surface behind to alpha = 0f since we're not interactive.
assertEquals(params.alpha, 0f)
assertEquals(0f, params.alpha)
assertTrue(params.matrix.isIdentity)
assertEquals("Wallpaper surface was expected to have opacity 0",
0f, captorWp.getLastValue().alpha)
verifyNoMoreInteractions(surfaceTransactionApplier)
}
@@ -317,19 +350,50 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
remoteAnimationTargets,
wallpaperTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(1f)
keyguardUnlockAnimationController.setWallpaperAppearAmount(1f)
val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
verify(surfaceTransactionApplier, times(1)).scheduleApply(captor.capture())
val captorSb = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, times(1)).scheduleApply(
captorSb.capture { sp -> sp.surface == surfaceControl1 })
val captorWp = ArgThatCaptor<SyncRtSurfaceTransactionApplier.SurfaceParams>()
verify(surfaceTransactionApplier, atLeastOnce().description("Wallpaper surface has not " +
"received scheduleApply")).scheduleApply(
captorWp.capture { sp -> sp.surface == surfaceControlWp })
val params = captor.value
assertEquals(params.alpha, 1f)
val params = captorSb.getLastValue()
assertEquals(1f, params.alpha)
assertTrue(params.matrix.isIdentity)
assertEquals("Wallpaper surface was expected to have opacity 1",
1f, captorWp.getLastValue().alpha)
verifyNoMoreInteractions(surfaceTransactionApplier)
}
private class ArgThatCaptor<T> {
private var allArgs: MutableList<T> = mutableListOf()
fun capture(predicate: Predicate<T>): T {
return argThat{x: T ->
if (predicate.test(x)) {
allArgs.add(x)
return@argThat true
}
return@argThat false
}
}
fun getLastValue(): T {
return allArgs.last()
}
fun getAllValues(): List<T> {
return allArgs
}
}
}