Merge "[flexiglass] Centralizes automatic scene changing business logic." into udc-qpr-dev
This commit is contained in:
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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() =
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user