diff options
10 files changed, 185 insertions, 26 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt index c4184905f28d..7a73c58ba193 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/BurnInState.kt @@ -73,7 +73,7 @@ private fun rememberBurnInParameters( BurnInParameters( clockControllerProvider = { clock }, topInset = topInset, - statusViewTop = topmostTop, + minViewY = topmostTop, ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt index 74fa46519629..b0f59fe68f11 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModelTest.kt @@ -127,7 +127,7 @@ class AodBurnInViewModelTest : SysuiTestCase() { @Test fun translationAndScale_whenFullyDozing() = testScope.runTest { - burnInParameters = burnInParameters.copy(statusViewTop = 100) + burnInParameters = burnInParameters.copy(minViewY = 100) val translationX by collectLastValue(underTest.translationX(burnInParameters)) val translationY by collectLastValue(underTest.translationY(burnInParameters)) val scale by collectLastValue(underTest.scale(burnInParameters)) @@ -182,11 +182,77 @@ class AodBurnInViewModelTest : SysuiTestCase() { } @Test - fun translationAndScale_whenFullyDozing_staysOutOfTopInset() = + fun translationAndScale_whenFullyDozing_MigrationFlagOff_staysOutOfTopInset() = testScope.runTest { + mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) + + burnInParameters = + burnInParameters.copy( + minViewY = 100, + topInset = 80, + ) + val translationX by collectLastValue(underTest.translationX(burnInParameters)) + val translationY by collectLastValue(underTest.translationY(burnInParameters)) + val scale by collectLastValue(underTest.scale(burnInParameters)) + + // Set to dozing (on AOD) + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + value = 1f, + transitionState = TransitionState.FINISHED + ), + validateStep = false, + ) + + // Trigger a change to the burn-in model + burnInFlow.value = + BurnInModel( + translationX = 20, + translationY = -30, + scale = 0.5f, + ) + assertThat(translationX).isEqualTo(20) + // -20 instead of -30, due to inset of 80 + assertThat(translationY).isEqualTo(-20) + assertThat(scale) + .isEqualTo( + BurnInScaleViewModel( + scale = 0.5f, + scaleClockOnly = true, + ) + ) + + // Set to the beginning of GONE->AOD transition + keyguardTransitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.AOD, + value = 0f, + transitionState = TransitionState.STARTED + ), + validateStep = false, + ) + assertThat(translationX).isEqualTo(0) + assertThat(translationY).isEqualTo(0) + assertThat(scale) + .isEqualTo( + BurnInScaleViewModel( + scale = 1f, + scaleClockOnly = true, + ) + ) + } + + @Test + fun translationAndScale_whenFullyDozing_MigrationFlagOn_staysOutOfTopInset() = + testScope.runTest { + mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) + burnInParameters = burnInParameters.copy( - statusViewTop = 100, + minViewY = 100, topInset = 80, ) val translationX by collectLastValue(underTest.translationX(burnInParameters)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt index c381749ec6d3..31b67b43adc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.kosmos.testScope +import com.android.systemui.shade.data.repository.fakeShadeRepository import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -42,6 +43,7 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() { val kosmos = testKosmos() val testScope = kosmos.testScope val repository = kosmos.fakeKeyguardTransitionRepository + val shadeRepository = kosmos.fakeShadeRepository val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository val underTest = kosmos.aodToLockscreenTransitionViewModel @@ -59,6 +61,38 @@ class AodToLockscreenTransitionViewModelTest : SysuiTestCase() { } @Test + fun notificationAlpha_whenShadeIsExpanded_equalsOne() = + testScope.runTest { + val alpha by collectLastValue(underTest.notificationAlpha) + + shadeRepository.setQsExpansion(0.5f) + runCurrent() + + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) + assertThat(alpha).isEqualTo(1f) + repository.sendTransitionStep(step(0.5f)) + assertThat(alpha).isEqualTo(1f) + repository.sendTransitionStep(step(1f)) + assertThat(alpha).isEqualTo(1f) + } + + @Test + fun notificationAlpha_whenShadeIsNotExpanded_usesTransitionValue() = + testScope.runTest { + val alpha by collectLastValue(underTest.notificationAlpha) + + shadeRepository.setQsExpansion(0f) + runCurrent() + + repository.sendTransitionStep(step(0f, TransitionState.STARTED)) + assertThat(alpha).isEqualTo(0f) + repository.sendTransitionStep(step(0.5f)) + assertThat(alpha).isEqualTo(0.5f) + repository.sendTransitionStep(step(1f)) + assertThat(alpha).isEqualTo(1f) + } + + @Test fun lockscreenAlphaStartsFromViewStateAccessorAlpha() = testScope.runTest { val viewState = ViewStateAccessor(alpha = { 0.5f }) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index 3e0082c5aed7..0641c610aaff 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -681,7 +681,7 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() { @Test fun shadeCollapseFadeIn() = testScope.runTest { - val fadeIn by collectLastValue(underTest.shadeCollpaseFadeIn) + val fadeIn by collectLastValue(underTest.shadeCollapseFadeIn) // Start on lockscreen without the shade underTest.setShadeCollapseFadeInComplete(false) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index ed488487f524..dc1f33d53853 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -25,6 +25,7 @@ import android.graphics.Rect import android.view.HapticFeedbackConstants import android.view.View import android.view.View.OnLayoutChangeListener +import android.view.View.VISIBLE import android.view.ViewGroup import android.view.ViewGroup.OnHierarchyChangeListener import android.view.ViewPropertyAnimator @@ -66,6 +67,7 @@ import com.android.systemui.util.ui.isAnimating import com.android.systemui.util.ui.stopAnimating import com.android.systemui.util.ui.value import javax.inject.Provider +import kotlin.math.min import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.coroutineScope @@ -345,7 +347,7 @@ object KeyguardRootViewBinder { } } - onLayoutChangeListener = OnLayoutChange(viewModel, burnInParams) + onLayoutChangeListener = OnLayoutChange(viewModel, childViews, burnInParams) view.addOnLayoutChangeListener(onLayoutChangeListener) // Views will be added or removed after the call to bind(). This is needed to avoid many @@ -405,6 +407,7 @@ object KeyguardRootViewBinder { private class OnLayoutChange( private val viewModel: KeyguardRootViewModel, + private val childViews: Map<Int, View>, private val burnInParams: MutableStateFlow<BurnInParameters>, ) : OnLayoutChangeListener { override fun onLayoutChange( @@ -418,7 +421,7 @@ object KeyguardRootViewBinder { oldRight: Int, oldBottom: Int ) { - view.findViewById<View>(R.id.nssl_placeholder)?.let { notificationListPlaceholder -> + childViews[R.id.nssl_placeholder]?.let { notificationListPlaceholder -> // After layout, ensure the notifications are positioned correctly viewModel.onNotificationContainerBoundsChanged( notificationListPlaceholder.top.toFloat(), @@ -426,10 +429,34 @@ object KeyguardRootViewBinder { ) } - view.findViewById<View>(R.id.keyguard_status_view)?.let { statusView -> - burnInParams.update { current -> current.copy(statusViewTop = statusView.top) } + burnInParams.update { current -> + current.copy( + minViewY = + if (migrateClocksToBlueprint()) { + // To ensure burn-in doesn't enroach the top inset, get the min top Y + childViews.entries.fold(Int.MAX_VALUE) { currentMin, (viewId, view) -> + min( + currentMin, + if (!isUserVisible(view)) { + Int.MAX_VALUE + } else { + view.getTop() + } + ) + } + } else { + childViews[R.id.keyguard_status_view]?.top ?: 0 + } + ) } } + + private fun isUserVisible(view: View): Boolean { + return view.id != R.id.burn_in_layer && + view.visibility == VISIBLE && + view.width > 0 && + view.height > 0 + } } suspend fun bindAodNotifIconVisibility( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt index 6fcbf48eab82..8665aabc3ef7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodBurnInViewModel.kt @@ -158,9 +158,9 @@ constructor( val burnInY = MathUtils.lerp(0, burnIn.translationY, interpolated).toInt() val translationY = if (Flags.migrateClocksToBlueprint()) { - burnInY + max(params.topInset - params.minViewY, burnInY) } else { - max(params.topInset, params.statusViewTop + burnInY) - params.statusViewTop + max(params.topInset, params.minViewY + burnInY) - params.minViewY } BurnInModel( @@ -194,8 +194,8 @@ data class BurnInParameters( val clockControllerProvider: Provider<ClockController>? = null, /** System insets that keyguard needs to stay out of */ val topInset: Int = 0, - /** Status view top, without translation added in */ - val statusViewTop: Int = 0, + /** The min y-value of the visible elements on lockscreen */ + val minViewY: Int = Int.MAX_VALUE, /** The current y translation of the view */ val translationY: () -> Float? = { null } ) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt index a0a77fbaee86..c40902871388 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt @@ -25,10 +25,13 @@ import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.keyguard.ui.StateToValue import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition +import com.android.systemui.shade.domain.interactor.ShadeInteractor import javax.inject.Inject import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map /** * Breaks down AOD->LOCKSCREEN transition into discrete steps for corresponding views to consume. @@ -39,6 +42,7 @@ class AodToLockscreenTransitionViewModel @Inject constructor( deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, + shadeInteractor: ShadeInteractor, animationFlow: KeyguardTransitionAnimationFlow, ) : DeviceEntryIconTransition { @@ -73,11 +77,22 @@ constructor( } val notificationAlpha: Flow<Float> = - transitionAnimation.sharedFlow( - duration = 500.milliseconds, - onStep = { it }, - onCancel = { 1f }, - ) + combine( + shadeInteractor.shadeExpansion.map { it > 0f }, + shadeInteractor.qsExpansion.map { it > 0f }, + transitionAnimation.sharedFlow( + duration = 500.milliseconds, + onStep = { it }, + onCancel = { 1f }, + ), + ) { isShadeExpanded, isQsExpanded, alpha -> + if (isShadeExpanded || isQsExpanded) { + // One example of this happening is dragging a notification while pulsing on AOD + 1f + } else { + alpha + } + } val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt index 77e146b98242..5191053b6f72 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt @@ -91,10 +91,10 @@ object SharedNotificationContainerBinder { if (!sceneContainerFlags.flexiNotifsEnabled()) { launch { // Only temporarily needed, until flexi notifs go live - viewModel.shadeCollpaseFadeIn.collect { fadeIn -> + viewModel.shadeCollapseFadeIn.collect { fadeIn -> if (fadeIn) { android.animation.ValueAnimator.ofFloat(0f, 1f).apply { - duration = 350 + duration = 250 addUpdateListener { animation -> controller.setMaxAlphaForExpansion( animation.getAnimatedFraction() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 6b949a3461e7..052e35c44bbe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -133,6 +133,21 @@ constructor( .distinctUntilChanged() .onStart { emit(false) } + /** + * Shade locked is a legacy concept, but necessary to mimic current functionality. Listen for + * both SHADE_LOCKED and shade/qs expansion in order to determine lock state, as one can arrive + * before the other. + */ + private val isShadeLocked: Flow<Boolean> = + combine( + keyguardInteractor.statusBarState.map { it == SHADE_LOCKED }, + shadeInteractor.qsExpansion.map { it > 0f }, + shadeInteractor.shadeExpansion.map { it > 0f }, + ) { isShadeLocked, isQsExpanded, isShadeExpanded -> + isShadeLocked && (isQsExpanded || isShadeExpanded) + } + .distinctUntilChanged() + val shadeCollapseFadeInComplete = MutableStateFlow(false) val configurationBasedDimensions: Flow<ConfigurationBasedDimensions> = @@ -190,7 +205,7 @@ constructor( ) /** Are we purely on the glanceable hub without the shade/qs? */ - internal val isOnGlanceableHubWithoutShade: Flow<Boolean> = + val isOnGlanceableHubWithoutShade: Flow<Boolean> = combine( communalInteractor.isIdleOnCommunal, // Shade with notifications @@ -208,12 +223,12 @@ constructor( ) /** Fade in only for use after the shade collapses */ - val shadeCollpaseFadeIn: Flow<Boolean> = + val shadeCollapseFadeIn: Flow<Boolean> = flow { while (currentCoroutineContext().isActive) { emit(false) // Wait for shade to be fully expanded - keyguardInteractor.statusBarState.first { it == SHADE_LOCKED } + isShadeLocked.first { it } // ... and then for it to be collapsed isOnLockscreenWithoutShade.first { it } emit(true) @@ -330,16 +345,16 @@ constructor( // shade expansion or swipe to dismiss combineTransform( isOnLockscreenWithoutShade, - shadeCollpaseFadeIn, + shadeCollapseFadeIn, alphaForShadeAndQsExpansion, keyguardInteractor.dismissAlpha, ) { isOnLockscreenWithoutShade, - shadeCollpaseFadeIn, + shadeCollapseFadeIn, alphaForShadeAndQsExpansion, dismissAlpha -> if (isOnLockscreenWithoutShade) { - if (!shadeCollpaseFadeIn && dismissAlpha != null) { + if (!shadeCollapseFadeIn && dismissAlpha != null) { emit(dismissAlpha) } } else { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt index 733340c67e55..460913f75eb4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelKosmos.kt @@ -22,11 +22,13 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsIntera import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.shade.domain.interactor.shadeInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.aodToLockscreenTransitionViewModel by Fixture { AodToLockscreenTransitionViewModel( deviceEntryUdfpsInteractor = deviceEntryUdfpsInteractor, + shadeInteractor = shadeInteractor, animationFlow = keyguardTransitionAnimationFlow, ) } |