summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt72
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt30
9 files changed, 196 insertions, 26 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 36458ede9506..15c9cf73d51d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -22,6 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -37,6 +38,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
@@ -242,7 +244,8 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
@Test
fun transitionValue() =
testScope.runTest {
- val startedSteps by collectValues(underTest.transitionValue(state = DOZING))
+ resetTransitionValueReplayCache(setOf(AOD, DOZING, LOCKSCREEN))
+ val transitionValues by collectValues(underTest.transitionValue(state = DOZING))
val toSteps =
listOf(
@@ -266,12 +269,14 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
runCurrent()
}
- assertThat(startedSteps).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f))
+ assertThat(transitionValues).isEqualTo(listOf(0f, 0.5f, 1f, 1f, 0.5f, 0f))
}
@Test
fun transitionValue_canceled_toAnotherState() =
testScope.runTest {
+ resetTransitionValueReplayCache(setOf(AOD, GONE, LOCKSCREEN))
+
val transitionValuesGone by collectValues(underTest.transitionValue(state = GONE))
val transitionValuesAod by collectValues(underTest.transitionValue(state = AOD))
val transitionValuesLs by collectValues(underTest.transitionValue(state = LOCKSCREEN))
@@ -297,6 +302,8 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
@Test
fun transitionValue_canceled_backToOriginalState() =
testScope.runTest {
+ resetTransitionValueReplayCache(setOf(AOD, GONE))
+
val transitionValuesGone by collectValues(underTest.transitionValue(state = GONE))
val transitionValuesAod by collectValues(underTest.transitionValue(state = AOD))
@@ -1395,4 +1402,8 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
testScope.runCurrent()
}
}
+
+ private fun resetTransitionValueReplayCache(states: Set<KeyguardState>) {
+ states.forEach { (underTest.transitionValue(it) as MutableSharedFlow).resetReplayCache() }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/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 cc7ebe96ba11..509a82da8eb9 100644
--- a/packages/SystemUI/multivalentTests/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
@@ -585,6 +585,12 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
notificationCount = 25
sharedNotificationContainerInteractor.notificationStackChanged()
assertThat(maxNotifications).isEqualTo(25)
+
+ // Also ensure another collection starts with the same value. As an example, folding
+ // then unfolding will restart the coroutine and it must get the last value immediately.
+ val newMaxNotifications by
+ collectLastValue(underTest.getMaxNotifications(calculateSpace))
+ assertThat(newMaxNotifications).isEqualTo(25)
}
@Test
@@ -760,6 +766,41 @@ class SharedNotificationContainerViewModelTest : SysuiTestCase() {
}
@Test
+ fun alphaDoesNotUpdateWhileOcclusionTransitionIsRunning() =
+ testScope.runTest {
+ val viewState = ViewStateAccessor()
+ val alpha by collectLastValue(underTest.keyguardAlpha(viewState))
+
+ showLockscreen()
+ // OCCLUDED transition gets to 90% complete
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.STARTED,
+ value = 0f,
+ )
+ )
+ runCurrent()
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ transitionState = TransitionState.RUNNING,
+ value = 0.9f,
+ )
+ )
+ runCurrent()
+
+ // At this point, alpha should be zero
+ assertThat(alpha).isEqualTo(0f)
+
+ // An attempt to override by the shade should be ignored
+ shadeRepository.setQsExpansion(0.5f)
+ assertThat(alpha).isEqualTo(0f)
+ }
+
+ @Test
fun alphaWhenGoneIsSetToOne() =
testScope.runTest {
val viewState = ViewStateAccessor()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index f10327e02240..a0ab8699ca48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -218,5 +218,6 @@ constructor(
private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 933.milliseconds
val TO_AOD_DURATION = DEFAULT_DURATION
+ val TO_DOZING_DURATION = DEFAULT_DURATION
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 0cd7d18b2342..804b630c3288 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -85,9 +85,11 @@ constructor(
private fun getTransitionValueFlow(state: KeyguardState): MutableSharedFlow<Float> {
return transitionValueCache.getOrPut(state) {
MutableSharedFlow<Float>(
- extraBufferCapacity = 2,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
+ replay = 1,
+ extraBufferCapacity = 2,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ .also { it.tryEmit(0f) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index 5337ca3b9be1..e8313a9f739c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -102,6 +102,7 @@ constructor(
private val lockscreenToPrimaryBouncerTransitionViewModel:
LockscreenToPrimaryBouncerTransitionViewModel,
private val occludedToAodTransitionViewModel: OccludedToAodTransitionViewModel,
+ private val occludedToDozingTransitionViewModel: OccludedToDozingTransitionViewModel,
private val occludedToLockscreenTransitionViewModel: OccludedToLockscreenTransitionViewModel,
private val primaryBouncerToAodTransitionViewModel: PrimaryBouncerToAodTransitionViewModel,
private val primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
@@ -228,6 +229,7 @@ constructor(
lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
occludedToAodTransitionViewModel.lockscreenAlpha,
+ occludedToDozingTransitionViewModel.lockscreenAlpha,
occludedToLockscreenTransitionViewModel.lockscreenAlpha,
primaryBouncerToAodTransitionViewModel.lockscreenAlpha,
primaryBouncerToGoneTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
new file mode 100644
index 000000000000..91554e3e914a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModel.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.FromOccludedTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Breaks down OCCLUDED->DOZING transition into discrete steps for corresponding views to consume.
+ */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class OccludedToDozingTransitionViewModel
+@Inject
+constructor(
+ animationFlow: KeyguardTransitionAnimationFlow,
+) {
+ private val transitionAnimation =
+ animationFlow.setup(
+ duration = FromOccludedTransitionInteractor.TO_DOZING_DURATION,
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.DOZING,
+ )
+
+ /** Lockscreen views alpha */
+ val lockscreenAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ startTime = 233.milliseconds,
+ duration = 250.milliseconds,
+ onStep = { it },
+ )
+}
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 f767b9997864..9df6f93df6a5 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
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
@@ -61,6 +62,8 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTran
import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.and
+import com.android.systemui.util.kotlin.BooleanFlowOperators.or
import com.android.systemui.util.kotlin.FlowDumperImpl
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import javax.inject.Inject
@@ -79,7 +82,6 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.transformWhile
import kotlinx.coroutines.isActive
@@ -123,6 +125,7 @@ constructor(
) : FlowDumperImpl(dumpManager) {
private val statesForConstrainedNotifications: Set<KeyguardState> =
setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER)
+ private val statesForHiddenKeyguard: Set<KeyguardState> = setOf(GONE, OCCLUDED)
/**
* Is either shade/qs expanded? This intentionally does not use the [ShadeInteractor] version,
@@ -194,8 +197,12 @@ constructor(
) { constrainedNotificationState, transitioningToOrFromLockscreen ->
constrainedNotificationState || transitioningToOrFromLockscreen
}
- .shareIn(scope = applicationScope, started = SharingStarted.Eagerly)
- .dumpWhileCollecting("isOnLockscreen")
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = false
+ )
+ .dumpValue("isOnLockscreen")
/** Are we purely on the keyguard without the shade/qs? */
val isOnLockscreenWithoutShade: Flow<Boolean> =
@@ -385,31 +392,54 @@ constructor(
.onStart { emit(1f) }
.dumpWhileCollecting("alphaForShadeAndQsExpansion")
- private val isGoneTransitionRunning: Flow<Boolean> =
+ private fun toFlowArray(
+ states: Set<KeyguardState>,
+ flow: (KeyguardState) -> Flow<Boolean>
+ ): Array<Flow<Boolean>> {
+ return states.map { flow(it) }.toTypedArray()
+ }
+
+ private val isTransitioningToHiddenKeyguard: Flow<Boolean> =
flow {
while (currentCoroutineContext().isActive) {
emit(false)
- // Ensure start where GONE is inactive
- keyguardTransitionInteractor.transitionValue(GONE).first { it == 0f }
- // Wait for a GONE transition to begin
- keyguardTransitionInteractor.transitionStepsToState(GONE).first {
- it.value > 0f && it.transitionState == RUNNING
- }
+ // Ensure states are inactive to start
+ and(
+ *toFlowArray(statesForHiddenKeyguard) { state ->
+ keyguardTransitionInteractor.transitionValue(state).map { it == 0f }
+ }
+ )
+ .first { it }
+ // Wait for a qualifying transition to begin
+ or(
+ *toFlowArray(statesForHiddenKeyguard) { state ->
+ keyguardTransitionInteractor
+ .transitionStepsToState(state)
+ .map { it.value > 0f && it.transitionState == RUNNING }
+ .onStart { emit(false) }
+ }
+ )
+ .first { it }
emit(true)
- // Now await the signal that SHADE state has been reached or the GONE transition
- // was reversed. Until SHADE state has been replaced and merged with GONE, it is
- // the only source of when it is considered safe to reset alpha to 1f for HUNs.
+ // Now await the signal that SHADE state has been reached or the transition was
+ // reversed. Until SHADE state has been replaced it is the only source of when
+ // it is considered safe to reset alpha to 1f for HUNs.
combine(
keyguardInteractor.statusBarState,
- // Emit -1f on start to make sure the flow runs
- keyguardTransitionInteractor.transitionValue(GONE).onStart { emit(-1f) }
- ) { statusBarState, goneValue ->
- statusBarState == SHADE || goneValue == 0f
+ and(
+ *toFlowArray(statesForHiddenKeyguard) { state ->
+ keyguardTransitionInteractor.transitionValue(state).map {
+ it == 0f
+ }
+ }
+ )
+ ) { statusBarState, stateIsReversed ->
+ statusBarState == SHADE || stateIsReversed
}
.first { it }
}
}
- .dumpWhileCollecting("goneTransitionInProgress")
+ .dumpWhileCollecting("isTransitioningToHiddenKeyguard")
fun keyguardAlpha(viewState: ViewStateAccessor): Flow<Float> {
// All transition view models are mututally exclusive, and safe to merge
@@ -440,7 +470,7 @@ constructor(
// shade expansion or swipe to dismiss
combineTransform(
isOnLockscreenWithoutShade,
- isGoneTransitionRunning,
+ isTransitioningToHiddenKeyguard,
shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
keyguardInteractor.dismissAlpha.dumpWhileCollecting(
@@ -448,7 +478,7 @@ constructor(
),
) {
isOnLockscreenWithoutShade,
- isGoneTransitionRunning,
+ isTransitioningToHiddenKeyguard,
shadeCollapseFadeIn,
alphaForShadeAndQsExpansion,
dismissAlpha ->
@@ -456,7 +486,7 @@ constructor(
if (!shadeCollapseFadeIn && dismissAlpha != null) {
emit(dismissAlpha)
}
- } else if (!isGoneTransitionRunning) {
+ } else if (!isTransitioningToHiddenKeyguard) {
emit(alphaForShadeAndQsExpansion)
}
},
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
index b91aafea9c38..f856d2700270 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelKosmos.kt
@@ -62,6 +62,7 @@ val Kosmos.keyguardRootViewModel by Fixture {
lockscreenToPrimaryBouncerTransitionViewModel =
lockscreenToPrimaryBouncerTransitionViewModel,
occludedToAodTransitionViewModel = occludedToAodTransitionViewModel,
+ occludedToDozingTransitionViewModel = occludedToDozingTransitionViewModel,
occludedToLockscreenTransitionViewModel = occludedToLockscreenTransitionViewModel,
primaryBouncerToAodTransitionViewModel = primaryBouncerToAodTransitionViewModel,
primaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
new file mode 100644
index 000000000000..a05e60672ef7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToDozingTransitionViewModelKosmos.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.occludedToDozingTransitionViewModel by Fixture {
+ OccludedToDozingTransitionViewModel(
+ animationFlow = keyguardTransitionAnimationFlow,
+ )
+}