Merge "[flexiglass] Centralizes automatic scene changing business logic." into udc-qpr-dev

This commit is contained in:
Ale Nijamkin
2023-07-11 17:03:33 +00:00
committed by Android (Google) Code Review
13 changed files with 328 additions and 188 deletions

View File

@@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -53,6 +52,7 @@ constructor(
private val repository: BouncerRepository,
private val authenticationInteractor: AuthenticationInteractor,
private val sceneInteractor: SceneInteractor,
featureFlags: FeatureFlags,
@Assisted private val containerName: String,
) {
@@ -95,36 +95,14 @@ constructor(
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
init {
// UNLOCKING SHOWS Gone.
//
// Move to the gone scene if the device becomes unlocked while on the bouncer scene.
applicationScope.launch {
sceneInteractor
.currentScene(containerName)
.flatMapLatest { currentScene ->
if (currentScene.key == SceneKey.Bouncer) {
authenticationInteractor.isUnlocked
} else {
flowOf(false)
if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
// Clear the message if moved from throttling to no-longer throttling.
applicationScope.launch {
isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) ->
if (wasThrottled && !currentlyThrottled) {
clearMessage()
}
}
.distinctUntilChanged()
.collect { isUnlocked ->
if (isUnlocked) {
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),
)
}
}
}
// Clear the message if moved from throttling to no-longer throttling.
applicationScope.launch {
isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) ->
if (wasThrottled && !currentlyThrottled) {
clearMessage()
}
}
}
}

View File

@@ -23,6 +23,8 @@ import com.android.systemui.R
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.util.kotlin.pairwise
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -49,6 +51,7 @@ constructor(
@Application private val applicationContext: Context,
@Application private val applicationScope: CoroutineScope,
interactorFactory: BouncerInteractor.Factory,
featureFlags: FeatureFlags,
@Assisted containerName: String,
) {
private val interactor: BouncerInteractor = interactorFactory.create(containerName)
@@ -102,15 +105,48 @@ constructor(
)
init {
applicationScope.launch {
_authMethod.subscriptionCount
.pairwise()
.map { (previousCount, currentCount) -> currentCount > previousCount }
.collect { subscriberAdded ->
if (subscriberAdded) {
reloadAuthMethod()
if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
applicationScope.launch {
interactor.isThrottled
.map { isThrottled ->
if (isThrottled) {
when (interactor.getAuthenticationMethod()) {
is AuthenticationMethodModel.Pin ->
R.string.kg_too_many_failed_pin_attempts_dialog_message
is AuthenticationMethodModel.Password ->
R.string.kg_too_many_failed_password_attempts_dialog_message
is AuthenticationMethodModel.Pattern ->
R.string.kg_too_many_failed_pattern_attempts_dialog_message
else -> null
}?.let { stringResourceId ->
applicationContext.getString(
stringResourceId,
interactor.throttling.value.failedAttemptCount,
ceil(interactor.throttling.value.remainingMs / 1000f).toInt(),
)
}
} else {
null
}
}
}
.distinctUntilChanged()
.collect { dialogMessageOrNull ->
if (dialogMessageOrNull != null) {
_throttlingDialogMessage.value = dialogMessageOrNull
}
}
}
applicationScope.launch {
_authMethod.subscriptionCount
.pairwise()
.map { (previousCount, currentCount) -> currentCount > previousCount }
.collect { subscriberAdded ->
if (subscriberAdded) {
reloadAuthMethod()
}
}
}
}
}
@@ -144,39 +180,6 @@ constructor(
*/
val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow()
init {
applicationScope.launch {
interactor.isThrottled
.map { isThrottled ->
if (isThrottled) {
when (interactor.getAuthenticationMethod()) {
is AuthenticationMethodModel.Pin ->
R.string.kg_too_many_failed_pin_attempts_dialog_message
is AuthenticationMethodModel.Password ->
R.string.kg_too_many_failed_password_attempts_dialog_message
is AuthenticationMethodModel.Pattern ->
R.string.kg_too_many_failed_pattern_attempts_dialog_message
else -> null
}?.let { stringResourceId ->
applicationContext.getString(
stringResourceId,
interactor.throttling.value.failedAttemptCount,
ceil(interactor.throttling.value.remainingMs / 1000f).toInt(),
)
}
} else {
null
}
}
.distinctUntilChanged()
.collect { dialogMessageOrNull ->
if (dialogMessageOrNull != null) {
_throttlingDialogMessage.value = dialogMessageOrNull
}
}
}
}
/** Notifies that the emergency services button was clicked. */
fun onEmergencyServicesButtonClicked() {
// TODO(b/280877228): implement this

View File

@@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
/** Hosts business and application state accessing logic for the lockscreen scene. */
class LockscreenSceneInteractor
@@ -42,7 +36,6 @@ constructor(
@Application applicationScope: CoroutineScope,
private val authenticationInteractor: AuthenticationInteractor,
bouncerInteractorFactory: BouncerInteractor.Factory,
private val sceneInteractor: SceneInteractor,
@Assisted private val containerName: String,
) {
private val bouncerInteractor: BouncerInteractor =
@@ -72,46 +65,6 @@ constructor(
initialValue = false,
)
init {
// LOCKING SHOWS Lockscreen.
//
// Move to the lockscreen scene if the device becomes locked while in any scene.
applicationScope.launch {
authenticationInteractor.isUnlocked
.map { !it }
.distinctUntilChanged()
.collect { isLocked ->
if (isLocked) {
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Lockscreen),
)
}
}
}
// BYPASS UNLOCK.
//
// Moves to the gone scene if bypass is enabled and the device becomes unlocked while in the
// lockscreen scene.
applicationScope.launch {
combine(
authenticationInteractor.isBypassEnabled,
authenticationInteractor.isUnlocked,
sceneInteractor.currentScene(containerName),
::Triple,
)
.collect { (isBypassEnabled, isUnlocked, currentScene) ->
if (isBypassEnabled && isUnlocked && currentScene.key == SceneKey.Lockscreen) {
sceneInteractor.setCurrentScene(
containerName = containerName,
scene = SceneModel(SceneKey.Gone),
)
}
}
}
}
/** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */
fun dismissLockscreen() {
bouncerInteractor.showOrUnlockDevice(containerName = containerName)

View File

@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -24,9 +25,11 @@ import com.android.systemui.flags.Flags
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneContainerNames
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -41,17 +44,19 @@ class SystemUiDefaultSceneContainerStartable
constructor(
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
private val authenticationInteractor: AuthenticationInteractor,
private val featureFlags: FeatureFlags,
) : CoreStartable {
override fun start() {
if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
keepVisibilityUpdated()
hydrateVisibility()
automaticallySwitchScenes()
}
}
/** Drives visibility of the scene container. */
private fun keepVisibilityUpdated() {
/** Updates the visibility of the scene container based on the current scene. */
private fun hydrateVisibility() {
applicationScope.launch {
sceneInteractor
.currentScene(CONTAINER_NAME)
@@ -63,6 +68,48 @@ constructor(
}
}
/** Switches between scenes based on ever-changing application state. */
private fun automaticallySwitchScenes() {
applicationScope.launch {
authenticationInteractor.isUnlocked
.map { isUnlocked ->
val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key
val isBypassEnabled = authenticationInteractor.isBypassEnabled.value
when {
isUnlocked ->
when (currentSceneKey) {
// When the device becomes unlocked in Bouncer, go to the Gone.
is SceneKey.Bouncer -> SceneKey.Gone
// When the device becomes unlocked in Lockscreen, go to Gone if
// bypass is enabled.
is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled }
// We got unlocked while on a scene that's not Lockscreen or
// Bouncer, no need to change scenes.
else -> null
}
// When the device becomes locked, to Lockscreen.
!isUnlocked ->
when (currentSceneKey) {
// Already on lockscreen or bouncer, no need to change scenes.
is SceneKey.Lockscreen,
is SceneKey.Bouncer -> null
// We got locked while on a scene that's not Lockscreen or Bouncer,
// go to Lockscreen.
else -> SceneKey.Lockscreen
}
else -> null
}
}
.filterNotNull()
.collect { targetSceneKey ->
sceneInteractor.setCurrentScene(
containerName = CONTAINER_NAME,
scene = SceneModel(targetSceneKey),
)
}
}
}
companion object {
private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT
}

View File

@@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
@Test
fun switchesToGone_whenUnlocked() =
testScope.runTest {
utils.authenticationRepository.setUnlocked(false)
sceneInteractor.setCurrentScene(
SceneTestUtils.CONTAINER_1,
SceneModel(SceneKey.Bouncer)
)
val currentScene by
collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
utils.authenticationRepository.setUnlocked(true)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
private fun assertTryAgainMessage(
message: String?,
time: Int,

View File

@@ -52,7 +52,10 @@ class BouncerViewModelTest : SysuiTestCase() {
authenticationInteractor = authenticationInteractor,
sceneInteractor = utils.sceneInteractor(),
)
private val underTest = utils.bouncerViewModel(bouncerInteractor)
private val underTest =
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
)
@Test
fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =

View File

@@ -20,7 +20,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -54,16 +53,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
BouncerViewModel(
applicationContext = context,
applicationScope = testScope.backgroundScope,
interactorFactory =
object : BouncerInteractor.Factory {
override fun create(containerName: String): BouncerInteractor {
return bouncerInteractor
}
},
containerName = SceneTestUtils.CONTAINER_1,
utils.bouncerViewModel(
bouncerInteractor = bouncerInteractor,
)
private val underTest =
PinBouncerViewModel(

View File

@@ -47,7 +47,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
private val underTest =
utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
@@ -128,22 +127,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@Test
fun deviceLockedInNonLockScreenScene_switchesToLockScreenScene() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
runCurrent()
sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
runCurrent()
utils.authenticationRepository.setUnlocked(true)
runCurrent()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
utils.authenticationRepository.setUnlocked(false)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
}
@Test
fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() =
testScope.runTest {

View File

@@ -56,7 +56,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
override fun create(containerName: String): LockscreenSceneInteractor {
return utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,

View File

@@ -53,7 +53,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
override fun create(containerName: String): LockscreenSceneInteractor {
return utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,

View File

@@ -14,14 +14,11 @@
* limitations under the License.
*/
@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class)
package com.android.systemui.scene.domain.startable
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneContainerNames
@@ -29,11 +26,14 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
@@ -41,29 +41,47 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
private val featureFlags = FakeFeatureFlags()
private val featureFlags = utils.featureFlags
private val authenticationRepository = utils.authenticationRepository()
private val authenticationInteractor =
utils.authenticationInteractor(
repository = authenticationRepository,
)
private val underTest =
SystemUiDefaultSceneContainerStartable(
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
authenticationInteractor = authenticationInteractor,
featureFlags = featureFlags,
)
@Before
fun setUp() {
prepareState()
}
@Test
fun start_featureEnabled_keepsVisibilityUpdated() =
fun hydrateVisibility_featureEnabled() =
testScope.runTest {
featureFlags.set(Flags.SCENE_CONTAINER, true)
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
val isVisible by
collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT))
prepareState(
isFeatureEnabled = true,
isDeviceUnlocked = true,
initialSceneKey = SceneKey.Gone,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
assertThat(isVisible).isTrue()
underTest.start()
sceneInteractor.setCurrentScene(
SceneContainerNames.SYSTEM_UI_DEFAULT,
SceneModel(SceneKey.Gone)
)
assertThat(isVisible).isFalse()
sceneInteractor.setCurrentScene(
@@ -74,14 +92,26 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
}
@Test
fun start_featureDisabled_doesNotUpdateVisibility() =
fun hydrateVisibility_featureDisabled() =
testScope.runTest {
featureFlags.set(Flags.SCENE_CONTAINER, false)
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
val isVisible by
collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT))
prepareState(
isFeatureEnabled = false,
isDeviceUnlocked = true,
initialSceneKey = SceneKey.Lockscreen,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
assertThat(isVisible).isTrue()
underTest.start()
assertThat(isVisible).isTrue()
sceneInteractor.setCurrentScene(
SceneContainerNames.SYSTEM_UI_DEFAULT,
@@ -95,4 +125,172 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
)
assertThat(isVisible).isTrue()
}
@Test
fun switchToLockscreenWhenDeviceLocks_featureEnabled() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = true,
isDeviceUnlocked = true,
initialSceneKey = SceneKey.Gone,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
underTest.start()
authenticationRepository.setUnlocked(false)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@Test
fun switchToLockscreenWhenDeviceLocks_featureDisabled() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = false,
isDeviceUnlocked = false,
initialSceneKey = SceneKey.Gone,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
underTest.start()
authenticationRepository.setUnlocked(false)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@Test
fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = true,
isDeviceUnlocked = false,
initialSceneKey = SceneKey.Bouncer,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
underTest.start()
authenticationRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@Test
fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = false,
isDeviceUnlocked = false,
initialSceneKey = SceneKey.Bouncer,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
underTest.start()
authenticationRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
}
@Test
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = true,
isBypassEnabled = true,
initialSceneKey = SceneKey.Lockscreen,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
authenticationRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
}
@Test
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = true,
isBypassEnabled = false,
initialSceneKey = SceneKey.Lockscreen,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
authenticationRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
@Test
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() =
testScope.runTest {
val currentSceneKey by
collectLastValue(
sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
it.key
}
)
prepareState(
isFeatureEnabled = false,
isBypassEnabled = true,
initialSceneKey = SceneKey.Lockscreen,
)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
underTest.start()
authenticationRepository.setUnlocked(true)
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
}
private fun prepareState(
isFeatureEnabled: Boolean = true,
isDeviceUnlocked: Boolean = false,
isBypassEnabled: Boolean = false,
initialSceneKey: SceneKey? = null,
) {
featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled)
authenticationRepository.setUnlocked(isDeviceUnlocked)
authenticationRepository.setBypassEnabled(isBypassEnabled)
initialSceneKey?.let {
sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it))
}
}
}

View File

@@ -22,7 +22,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.SceneTestUtils.Companion.CONTAINER_1
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
@@ -54,7 +53,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
override fun create(containerName: String): LockscreenSceneInteractor {
return utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
@@ -89,7 +87,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
@Test
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
val currentScene by
collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(true)
runCurrent()
@@ -102,7 +101,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
@Test
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
val currentScene by
collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
runCurrent()

View File

@@ -24,6 +24,8 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -51,6 +53,7 @@ class SceneTestUtils(
) {
val testDispatcher = StandardTestDispatcher()
val testScope = TestScope(testDispatcher)
val featureFlags = FakeFeatureFlags().apply { set(Flags.SCENE_CONTAINER, true) }
private val userRepository: UserRepository by lazy {
FakeUserRepository().apply {
val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
@@ -129,6 +132,7 @@ class SceneTestUtils(
repository = BouncerRepository(),
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
featureFlags = featureFlags,
containerName = CONTAINER_1,
)
}
@@ -145,13 +149,13 @@ class SceneTestUtils(
return bouncerInteractor
}
},
featureFlags = featureFlags,
containerName = CONTAINER_1,
)
}
fun lockScreenSceneInteractor(
authenticationInteractor: AuthenticationInteractor,
sceneInteractor: SceneInteractor,
bouncerInteractor: BouncerInteractor,
): LockscreenSceneInteractor {
return LockscreenSceneInteractor(
@@ -163,7 +167,6 @@ class SceneTestUtils(
return bouncerInteractor
}
},
sceneInteractor = sceneInteractor,
containerName = CONTAINER_1,
)
}