[flexiglass] Use transitionState instead of currentScene.
There's often a mismatch between the currentScene and the UI because currentScene drives the UI (when programmatic scene changes are requested) but also the UI updates currentScene (when a user action completes). This CL simplifies the API surface such that external consumers of the Flexiglass API can use the transitionState, driven directly from the UI to know about transition pairs and progress of transitions (vs. idle). Unfortunately, I did have to keep currentScene (renamed to "desiredScene" to better capture its role in the system) so it can be served downstream to the UI for servicing programmatic scene changes. In addition, many of the existing consumers of Flexiglass were updated to no longer rely on currentScene as it would (a) often mismatch what the UI is showing and (b) not provide enough context to know about the state of transitions. Fix: 294220005 Test: Unit tests updated Test: Manually verified with auth method None, Swipe, and Pattern - on, unlock, lock, swipe down to reveal shade and QS, swipe up to reveal bouncer, back gesture to return to lockscreen. Change-Id: I3de051f7c0331f39679c15ec0c099cd574b68935
This commit is contained in:
@@ -77,7 +77,7 @@ fun SceneContainer(
|
||||
|
||||
SceneTransitionLayout(
|
||||
currentScene = currentSceneKey.toTransitionSceneKey(),
|
||||
onChangeScene = { sceneKey -> viewModel.setCurrentScene(sceneKey.toModel()) },
|
||||
onChangeScene = viewModel::onSceneChanged,
|
||||
transitions = transitions {},
|
||||
state = state,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
@@ -154,3 +154,7 @@ private fun UserAction.toTransitionUserAction(): SceneTransitionUserAction {
|
||||
is UserAction.Back -> Back
|
||||
}
|
||||
}
|
||||
|
||||
private fun SceneContainerViewModel.onSceneChanged(sceneKey: SceneTransitionSceneKey) {
|
||||
onSceneChanged(sceneKey.toModel())
|
||||
}
|
||||
|
||||
@@ -476,18 +476,16 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
|
||||
if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
|
||||
// When the scene framework transitions from bouncer to gone, we dismiss the keyguard.
|
||||
mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow(
|
||||
mSceneInteractor.get().getTransitions(),
|
||||
sceneTransitionModel -> {
|
||||
if (sceneTransitionModel != null
|
||||
&& sceneTransitionModel.getFrom() == SceneKey.Bouncer.INSTANCE
|
||||
&& sceneTransitionModel.getTo() == SceneKey.Gone.INSTANCE) {
|
||||
final int selectedUserId = mUserInteractor.getSelectedUserId();
|
||||
showNextSecurityScreenOrFinish(
|
||||
/* authenticated= */ true,
|
||||
selectedUserId,
|
||||
/* bypassSecondaryLockScreen= */ true,
|
||||
mSecurityModel.getSecurityMode(selectedUserId));
|
||||
}
|
||||
mSceneInteractor.get().finishedSceneTransitions(
|
||||
/* from= */ SceneKey.Bouncer.INSTANCE,
|
||||
/* to= */ SceneKey.Gone.INSTANCE),
|
||||
unused -> {
|
||||
final int selectedUserId = mUserInteractor.getSelectedUserId();
|
||||
showNextSecurityScreenOrFinish(
|
||||
/* authenticated= */ true,
|
||||
selectedUserId,
|
||||
/* bypassSecondaryLockScreen= */ true,
|
||||
mSecurityModel.getSecurityMode(selectedUserId));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,12 +116,12 @@ constructor(
|
||||
repository.setMessage(
|
||||
message ?: promptMessage(authenticationInteractor.getAuthenticationMethod())
|
||||
)
|
||||
sceneInteractor.setCurrentScene(
|
||||
sceneInteractor.changeScene(
|
||||
scene = SceneModel(SceneKey.Bouncer),
|
||||
loggingReason = "request to unlock device while authentication required",
|
||||
)
|
||||
} else {
|
||||
sceneInteractor.setCurrentScene(
|
||||
sceneInteractor.changeScene(
|
||||
scene = SceneModel(SceneKey.Gone),
|
||||
loggingReason = "request to unlock device while authentication isn't required",
|
||||
)
|
||||
@@ -169,7 +169,7 @@ constructor(
|
||||
authenticationInteractor.authenticate(input, tryAutoConfirm) ?: return null
|
||||
|
||||
if (isAuthenticated) {
|
||||
sceneInteractor.setCurrentScene(
|
||||
sceneInteractor.changeScene(
|
||||
scene = SceneModel(SceneKey.Gone),
|
||||
loggingReason = "successful authentication",
|
||||
)
|
||||
|
||||
@@ -18,50 +18,49 @@
|
||||
|
||||
package com.android.systemui.scene.data.repository
|
||||
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.SceneContainerConfig
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.scene.shared.model.SceneTransitionModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/** Source of truth for scene framework application state. */
|
||||
class SceneContainerRepository
|
||||
@Inject
|
||||
constructor(
|
||||
@Application applicationScope: CoroutineScope,
|
||||
private val config: SceneContainerConfig,
|
||||
) {
|
||||
private val _desiredScene = MutableStateFlow(SceneModel(config.initialSceneKey))
|
||||
val desiredScene: StateFlow<SceneModel> = _desiredScene.asStateFlow()
|
||||
|
||||
private val _isVisible = MutableStateFlow(true)
|
||||
val isVisible: StateFlow<Boolean> = _isVisible.asStateFlow()
|
||||
|
||||
private val _currentScene = MutableStateFlow(SceneModel(config.initialSceneKey))
|
||||
val currentScene: StateFlow<SceneModel> = _currentScene.asStateFlow()
|
||||
|
||||
private val transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
|
||||
val transitionProgress: Flow<Float> =
|
||||
transitionState.flatMapLatest { observableTransitionStateFlow ->
|
||||
observableTransitionStateFlow?.flatMapLatest { observableTransitionState ->
|
||||
when (observableTransitionState) {
|
||||
is ObservableTransitionState.Idle -> flowOf(1f)
|
||||
is ObservableTransitionState.Transition -> observableTransitionState.progress
|
||||
}
|
||||
}
|
||||
?: flowOf(1f)
|
||||
}
|
||||
|
||||
private val _transitions = MutableStateFlow<SceneTransitionModel?>(null)
|
||||
val transitions: StateFlow<SceneTransitionModel?> = _transitions.asStateFlow()
|
||||
private val defaultTransitionState = ObservableTransitionState.Idle(config.initialSceneKey)
|
||||
private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
|
||||
val transitionState: StateFlow<ObservableTransitionState> =
|
||||
_transitionState
|
||||
.flatMapLatest { innerFlowOrNull -> innerFlowOrNull ?: flowOf(defaultTransitionState) }
|
||||
.stateIn(
|
||||
scope = applicationScope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = defaultTransitionState,
|
||||
)
|
||||
|
||||
/**
|
||||
* Returns the keys to all scenes in the container with the given name.
|
||||
* Returns the keys to all scenes in the container.
|
||||
*
|
||||
* The scenes will be sorted in z-order such that the last one is the one that should be
|
||||
* rendered on top of all previous ones.
|
||||
@@ -70,40 +69,19 @@ constructor(
|
||||
return config.sceneKeys
|
||||
}
|
||||
|
||||
/** Sets the current scene in the container with the given name. */
|
||||
fun setCurrentScene(scene: SceneModel) {
|
||||
fun setDesiredScene(scene: SceneModel) {
|
||||
check(allSceneKeys().contains(scene.key)) {
|
||||
"""
|
||||
Cannot set current scene key to "${scene.key}". The configuration does not contain a
|
||||
scene with that key.
|
||||
Cannot set the desired scene key to "${scene.key}". The configuration does not
|
||||
contain a scene with that key.
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
|
||||
_currentScene.value = scene
|
||||
_desiredScene.value = scene
|
||||
}
|
||||
|
||||
/** Sets the scene transition in the container with the given name. */
|
||||
fun setSceneTransition(from: SceneKey, to: SceneKey) {
|
||||
check(allSceneKeys().contains(from)) {
|
||||
"""
|
||||
Cannot set current scene key to "$from". The configuration does not contain a scene
|
||||
with that key.
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
check(allSceneKeys().contains(to)) {
|
||||
"""
|
||||
Cannot set current scene key to "$to". The configuration does not contain a scene
|
||||
with that key.
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
|
||||
_transitions.value = SceneTransitionModel(from = from, to = to)
|
||||
}
|
||||
|
||||
/** Sets whether the container with the given name is visible. */
|
||||
/** Sets whether the container is visible. */
|
||||
fun setVisible(isVisible: Boolean) {
|
||||
_isVisible.value = isVisible
|
||||
}
|
||||
@@ -114,6 +92,6 @@ constructor(
|
||||
* Note that you must call is with `null` when the UI is done or risk a memory leak.
|
||||
*/
|
||||
fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
|
||||
this.transitionState.value = transitionState
|
||||
_transitionState.value = transitionState
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,15 @@ import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.RemoteUserInput
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.scene.shared.model.SceneTransitionModel
|
||||
import com.android.systemui.util.kotlin.pairwise
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
|
||||
/**
|
||||
* Generic business logic and app state accessors for the scene framework.
|
||||
@@ -46,7 +49,54 @@ constructor(
|
||||
) {
|
||||
|
||||
/**
|
||||
* Returns the keys of all scenes in the container with the given name.
|
||||
* The currently *desired* scene.
|
||||
*
|
||||
* **Important:** this value will _commonly be different_ from what is being rendered in the UI,
|
||||
* by design.
|
||||
*
|
||||
* There are two intended sources for this value:
|
||||
* 1. Programmatic requests to transition to another scene (calls to [changeScene]).
|
||||
* 2. Reports from the UI about completing a transition to another scene (calls to
|
||||
* [onSceneChanged]).
|
||||
*
|
||||
* Both the sources above cause the value of this flow to change; however, they cause mismatches
|
||||
* in different ways.
|
||||
*
|
||||
* **Updates from programmatic transitions**
|
||||
*
|
||||
* When an external bit of code asks the framework to switch to another scene, the value here
|
||||
* will update immediately. Downstream, the UI will detect this change and initiate the
|
||||
* transition animation. As the transition animation progresses, a threshold will be reached, at
|
||||
* which point the UI and the state here will match each other.
|
||||
*
|
||||
* **Updates from the UI**
|
||||
*
|
||||
* When the user interacts with the UI, the UI runs a transition animation that tracks the user
|
||||
* pointer (for example, the user's finger). During this time, the state value here and what the
|
||||
* UI shows will likely not match. Once/if a threshold is met, the UI reports it and commits the
|
||||
* change, making the value here match the UI again.
|
||||
*/
|
||||
val desiredScene: StateFlow<SceneModel> = repository.desiredScene
|
||||
|
||||
/**
|
||||
* The current state of the transition.
|
||||
*
|
||||
* Consumers should use this state to know:
|
||||
* 1. Whether there is an ongoing transition or if the system is at rest.
|
||||
* 2. When transitioning, which scenes are being transitioned between.
|
||||
* 3. When transitioning, what the progress of the transition is.
|
||||
*/
|
||||
val transitionState: StateFlow<ObservableTransitionState> = repository.transitionState
|
||||
|
||||
/** Whether the scene container is visible. */
|
||||
val isVisible: StateFlow<Boolean> = repository.isVisible
|
||||
|
||||
private val _remoteUserInput: MutableStateFlow<RemoteUserInput?> = MutableStateFlow(null)
|
||||
/** A flow of motion events originating from outside of the scene framework. */
|
||||
val remoteUserInput: StateFlow<RemoteUserInput?> = _remoteUserInput.asStateFlow()
|
||||
|
||||
/**
|
||||
* Returns the keys of all scenes in the container.
|
||||
*
|
||||
* The scenes will be sorted in z-order such that the last one is the one that should be
|
||||
* rendered on top of all previous ones.
|
||||
@@ -55,26 +105,20 @@ constructor(
|
||||
return repository.allSceneKeys()
|
||||
}
|
||||
|
||||
/** Sets the scene in the container with the given name. */
|
||||
fun setCurrentScene(scene: SceneModel, loggingReason: String) {
|
||||
val currentSceneKey = repository.currentScene.value.key
|
||||
if (currentSceneKey == scene.key) {
|
||||
return
|
||||
}
|
||||
|
||||
logger.logSceneChange(
|
||||
from = currentSceneKey,
|
||||
to = scene.key,
|
||||
reason = loggingReason,
|
||||
)
|
||||
repository.setCurrentScene(scene)
|
||||
repository.setSceneTransition(from = currentSceneKey, to = scene.key)
|
||||
/**
|
||||
* Requests a scene change to the given scene.
|
||||
*
|
||||
* The change is animated. Therefore, while the value in [desiredScene] will update immediately,
|
||||
* it will be some time before the UI will switch to the desired scene. The scene change
|
||||
* requested is remembered here but served by the UI layer, which will start a transition
|
||||
* animation. Once enough of the transition has occurred, the system will come into agreement
|
||||
* between the [desiredScene] and the UI.
|
||||
*/
|
||||
fun changeScene(scene: SceneModel, loggingReason: String) {
|
||||
updateDesiredScene(scene, loggingReason, logger::logSceneChangeRequested)
|
||||
}
|
||||
|
||||
/** The current scene in the container with the given name. */
|
||||
val currentScene: StateFlow<SceneModel> = repository.currentScene
|
||||
|
||||
/** Sets the visibility of the container with the given name. */
|
||||
/** Sets the visibility of the container. */
|
||||
fun setVisible(isVisible: Boolean, loggingReason: String) {
|
||||
val wasVisible = repository.isVisible.value
|
||||
if (wasVisible == isVisible) {
|
||||
@@ -89,9 +133,6 @@ constructor(
|
||||
return repository.setVisible(isVisible)
|
||||
}
|
||||
|
||||
/** Whether the container with the given name is visible. */
|
||||
val isVisible: StateFlow<Boolean> = repository.isVisible
|
||||
|
||||
/**
|
||||
* Binds the given flow so the system remembers it.
|
||||
*
|
||||
@@ -101,23 +142,53 @@ constructor(
|
||||
repository.setTransitionState(transitionState)
|
||||
}
|
||||
|
||||
/** Progress of the transition into the current scene in the container with the given name. */
|
||||
val transitionProgress: Flow<Float> = repository.transitionProgress
|
||||
|
||||
/**
|
||||
* Scene transitions as pairs of keys. A new value is emitted exactly once, each time a scene
|
||||
* transition occurs. The flow begins with a `null` value at first, because the initial scene is
|
||||
* not something that we transition to from another scene.
|
||||
* Returns a stream of events that emits one [Unit] every time the framework transitions from
|
||||
* [from] to [to].
|
||||
*/
|
||||
val transitions: StateFlow<SceneTransitionModel?> = repository.transitions
|
||||
|
||||
private val _remoteUserInput: MutableStateFlow<RemoteUserInput?> = MutableStateFlow(null)
|
||||
|
||||
/** A flow of motion events originating from outside of the scene framework. */
|
||||
val remoteUserInput: StateFlow<RemoteUserInput?> = _remoteUserInput.asStateFlow()
|
||||
fun finishedSceneTransitions(from: SceneKey, to: SceneKey): Flow<Unit> {
|
||||
return transitionState
|
||||
.mapNotNull { it as? ObservableTransitionState.Idle }
|
||||
.map { idleState -> idleState.scene }
|
||||
.distinctUntilChanged()
|
||||
.pairwise()
|
||||
.mapNotNull { (previousSceneKey, currentSceneKey) ->
|
||||
Unit.takeIf { previousSceneKey == from && currentSceneKey == to }
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles a remote user input. */
|
||||
fun onRemoteUserInput(input: RemoteUserInput) {
|
||||
_remoteUserInput.value = input
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies that the UI has transitioned sufficiently to the given scene.
|
||||
*
|
||||
* *Not intended for external use!*
|
||||
*
|
||||
* Once a transition between one scene and another passes a threshold, the UI invokes this
|
||||
* method to report it, updating the value in [desiredScene] to match what the UI shows.
|
||||
*/
|
||||
internal fun onSceneChanged(scene: SceneModel, loggingReason: String) {
|
||||
updateDesiredScene(scene, loggingReason, logger::logSceneChangeCommitted)
|
||||
}
|
||||
|
||||
private fun updateDesiredScene(
|
||||
scene: SceneModel,
|
||||
loggingReason: String,
|
||||
log: (from: SceneKey, to: SceneKey, loggingReason: String) -> Unit,
|
||||
) {
|
||||
val currentSceneKey = desiredScene.value.key
|
||||
if (currentSceneKey == scene.key) {
|
||||
return
|
||||
}
|
||||
|
||||
log(
|
||||
/* from= */ currentSceneKey,
|
||||
/* to= */ scene.key,
|
||||
/* loggingReason= */ loggingReason,
|
||||
)
|
||||
repository.setDesiredScene(scene)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.android.systemui.model.SysUiState
|
||||
import com.android.systemui.model.updateFlags
|
||||
import com.android.systemui.scene.domain.interactor.SceneInteractor
|
||||
import com.android.systemui.scene.shared.logger.SceneLogger
|
||||
import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING
|
||||
@@ -40,8 +41,8 @@ import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_B
|
||||
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.flow.mapNotNull
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
@@ -73,14 +74,31 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the visibility of the scene container based on the current scene. */
|
||||
/** Updates the visibility of the scene container. */
|
||||
private fun hydrateVisibility() {
|
||||
applicationScope.launch {
|
||||
sceneInteractor.currentScene
|
||||
.map { it.key }
|
||||
sceneInteractor.transitionState
|
||||
.mapNotNull { state ->
|
||||
when (state) {
|
||||
is ObservableTransitionState.Idle -> {
|
||||
if (state.scene != SceneKey.Gone) {
|
||||
true to "scene is not Gone"
|
||||
} else {
|
||||
false to "scene is Gone"
|
||||
}
|
||||
}
|
||||
is ObservableTransitionState.Transition -> {
|
||||
if (state.fromScene == SceneKey.Gone) {
|
||||
true to "scene transitioning away from Gone"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.collect { sceneKey ->
|
||||
sceneInteractor.setVisible(sceneKey != SceneKey.Gone, "scene is $sceneKey")
|
||||
.collect { (isVisible, loggingReason) ->
|
||||
sceneInteractor.setVisible(isVisible, loggingReason)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,43 +107,55 @@ constructor(
|
||||
private fun automaticallySwitchScenes() {
|
||||
applicationScope.launch {
|
||||
authenticationInteractor.isUnlocked
|
||||
.map { isUnlocked ->
|
||||
val currentSceneKey = sceneInteractor.currentScene.value.key
|
||||
.mapNotNull { isUnlocked ->
|
||||
val renderedScenes =
|
||||
when (val transitionState = sceneInteractor.transitionState.value) {
|
||||
is ObservableTransitionState.Idle -> setOf(transitionState.scene)
|
||||
is ObservableTransitionState.Transition ->
|
||||
setOf(
|
||||
transitionState.progress,
|
||||
transitionState.toScene,
|
||||
)
|
||||
}
|
||||
val isBypassEnabled = authenticationInteractor.isBypassEnabled()
|
||||
when {
|
||||
isUnlocked ->
|
||||
when (currentSceneKey) {
|
||||
when {
|
||||
// When the device becomes unlocked in Bouncer, go to Gone.
|
||||
is SceneKey.Bouncer ->
|
||||
renderedScenes.contains(SceneKey.Bouncer) ->
|
||||
SceneKey.Gone to "device unlocked in Bouncer scene"
|
||||
|
||||
// When the device becomes unlocked in Lockscreen, go to Gone if
|
||||
// bypass is enabled.
|
||||
is SceneKey.Lockscreen ->
|
||||
renderedScenes.contains(SceneKey.Lockscreen) ->
|
||||
if (isBypassEnabled) {
|
||||
SceneKey.Gone to
|
||||
"device unlocked in Lockscreen scene with bypass"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// 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) {
|
||||
when {
|
||||
// Already on lockscreen or bouncer, no need to change scenes.
|
||||
is SceneKey.Lockscreen,
|
||||
is SceneKey.Bouncer -> null
|
||||
renderedScenes.contains(SceneKey.Lockscreen) ||
|
||||
renderedScenes.contains(SceneKey.Bouncer) -> null
|
||||
|
||||
// We got locked while on a scene that's not Lockscreen or Bouncer,
|
||||
// go to Lockscreen.
|
||||
else ->
|
||||
SceneKey.Lockscreen to "device locked in $currentSceneKey scene"
|
||||
SceneKey.Lockscreen to
|
||||
"device locked in non-Lockscreen and non-Bouncer scene"
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
.filterNotNull()
|
||||
.collect { (targetSceneKey, loggingReason) ->
|
||||
switchToScene(
|
||||
targetSceneKey = targetSceneKey,
|
||||
@@ -143,7 +173,7 @@ constructor(
|
||||
WakefulnessState.STARTING_TO_SLEEP -> {
|
||||
switchToScene(
|
||||
targetSceneKey = SceneKey.Lockscreen,
|
||||
loggingReason = "device is asleep",
|
||||
loggingReason = "device is starting to sleep",
|
||||
)
|
||||
}
|
||||
WakefulnessState.STARTING_TO_WAKE -> {
|
||||
@@ -165,8 +195,9 @@ constructor(
|
||||
/** Keeps [SysUiState] up-to-date */
|
||||
private fun hydrateSystemUiState() {
|
||||
applicationScope.launch {
|
||||
sceneInteractor.currentScene
|
||||
.map { it.key }
|
||||
sceneInteractor.transitionState
|
||||
.mapNotNull { it as? ObservableTransitionState.Idle }
|
||||
.map { it.scene }
|
||||
.distinctUntilChanged()
|
||||
.collect { sceneKey ->
|
||||
sysUiState.updateFlags(
|
||||
@@ -183,7 +214,7 @@ constructor(
|
||||
}
|
||||
|
||||
private fun switchToScene(targetSceneKey: SceneKey, loggingReason: String) {
|
||||
sceneInteractor.setCurrentScene(
|
||||
sceneInteractor.changeScene(
|
||||
scene = SceneModel(targetSceneKey),
|
||||
loggingReason = loggingReason,
|
||||
)
|
||||
|
||||
@@ -37,7 +37,7 @@ class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer:
|
||||
)
|
||||
}
|
||||
|
||||
fun logSceneChange(
|
||||
fun logSceneChangeRequested(
|
||||
from: SceneKey,
|
||||
to: SceneKey,
|
||||
reason: String,
|
||||
@@ -50,7 +50,24 @@ class SceneLogger @Inject constructor(@SceneFrameworkLog private val logBuffer:
|
||||
str2 = to.toString()
|
||||
str3 = reason
|
||||
},
|
||||
messagePrinter = { "$str1 → $str2, reason: $str3" },
|
||||
messagePrinter = { "Scene change requested: $str1 → $str2, reason: $str3" },
|
||||
)
|
||||
}
|
||||
|
||||
fun logSceneChangeCommitted(
|
||||
from: SceneKey,
|
||||
to: SceneKey,
|
||||
reason: String,
|
||||
) {
|
||||
logBuffer.log(
|
||||
tag = TAG,
|
||||
level = LogLevel.INFO,
|
||||
messageInitializer = {
|
||||
str1 = from.toString()
|
||||
str2 = to.toString()
|
||||
str3 = reason
|
||||
},
|
||||
messagePrinter = { "Scene change committed: $str1 → $str2, reason: $str3" },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.scene.shared.model
|
||||
|
||||
/** Models a transition between two scenes. */
|
||||
data class SceneTransitionModel(
|
||||
/** The scene we transitioned away from. */
|
||||
val from: SceneKey,
|
||||
/** The scene we transitioned into. */
|
||||
val to: SceneKey,
|
||||
)
|
||||
@@ -45,15 +45,15 @@ constructor(
|
||||
*/
|
||||
val allSceneKeys: List<SceneKey> = interactor.allSceneKeys()
|
||||
|
||||
/** The current scene. */
|
||||
val currentScene: StateFlow<SceneModel> = interactor.currentScene
|
||||
/** The scene that should be rendered. */
|
||||
val currentScene: StateFlow<SceneModel> = interactor.desiredScene
|
||||
|
||||
/** Whether the container is visible. */
|
||||
val isVisible: StateFlow<Boolean> = interactor.isVisible
|
||||
|
||||
/** Requests a transition to the scene with the given key. */
|
||||
fun setCurrentScene(scene: SceneModel) {
|
||||
interactor.setCurrentScene(
|
||||
/** Notifies that the UI has transitioned sufficiently to the given scene. */
|
||||
fun onSceneChanged(scene: SceneModel) {
|
||||
interactor.onSceneChanged(
|
||||
scene = scene,
|
||||
loggingReason = SCENE_TRANSITION_LOGGING_REASON,
|
||||
)
|
||||
|
||||
@@ -50,6 +50,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.scene.SceneTestUtils
|
||||
import com.android.systemui.scene.domain.interactor.SceneInteractor
|
||||
import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
@@ -66,6 +67,8 @@ import com.google.common.truth.Truth
|
||||
import java.util.Optional
|
||||
import junit.framework.Assert
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
@@ -139,6 +142,7 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
private lateinit var testableResources: TestableResources
|
||||
private lateinit var sceneTestUtils: SceneTestUtils
|
||||
private lateinit var sceneInteractor: SceneInteractor
|
||||
private lateinit var sceneTransitionStateFlow: MutableStateFlow<ObservableTransitionState>
|
||||
|
||||
private lateinit var underTest: KeyguardSecurityContainerController
|
||||
|
||||
@@ -198,6 +202,9 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
whenever(userInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID)
|
||||
sceneTestUtils = SceneTestUtils(this)
|
||||
sceneInteractor = sceneTestUtils.sceneInteractor()
|
||||
sceneTransitionStateFlow =
|
||||
MutableStateFlow(ObservableTransitionState.Idle(SceneKey.Lockscreen))
|
||||
sceneInteractor.setTransitionState(sceneTransitionStateFlow)
|
||||
|
||||
underTest =
|
||||
KeyguardSecurityContainerController(
|
||||
@@ -733,20 +740,39 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
// is
|
||||
// not enough to trigger a dismissal of the keyguard.
|
||||
underTest.onViewAttached()
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneTransitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(
|
||||
SceneKey.Lockscreen,
|
||||
SceneKey.Bouncer,
|
||||
flowOf(.5f)
|
||||
)
|
||||
runCurrent()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
|
||||
runCurrent()
|
||||
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
|
||||
|
||||
// While listening, going from the bouncer scene to the gone scene, does dismiss the
|
||||
// keyguard.
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneTransitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
|
||||
runCurrent()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
|
||||
runCurrent()
|
||||
verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt())
|
||||
|
||||
// While listening, moving back to the bouncer scene does not dismiss the keyguard
|
||||
// again.
|
||||
clearInvocations(viewMediatorCallback)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneTransitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f))
|
||||
runCurrent()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
|
||||
runCurrent()
|
||||
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
|
||||
|
||||
@@ -754,12 +780,22 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
// scene
|
||||
// does not dismiss the keyguard while we're not listening.
|
||||
underTest.onViewDetached()
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneTransitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
|
||||
runCurrent()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
|
||||
runCurrent()
|
||||
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
|
||||
|
||||
// While not listening, moving back to the bouncer does not dismiss the keyguard.
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneTransitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(SceneKey.Gone, SceneKey.Bouncer, flowOf(.5f))
|
||||
runCurrent()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
|
||||
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Bouncer)
|
||||
runCurrent()
|
||||
verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt())
|
||||
|
||||
@@ -767,7 +803,12 @@ class KeyguardSecurityContainerControllerTest : SysuiTestCase() {
|
||||
// gone
|
||||
// scene now does dismiss the keyguard again.
|
||||
underTest.onViewAttached()
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneTransitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(SceneKey.Bouncer, SceneKey.Gone, flowOf(.5f))
|
||||
runCurrent()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
|
||||
sceneTransitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
|
||||
runCurrent()
|
||||
verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt())
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun pinAuthMethod() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(underTest.message)
|
||||
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
@@ -102,7 +102,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(underTest.message)
|
||||
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
@@ -139,7 +139,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(underTest.message)
|
||||
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
@@ -169,7 +169,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun passwordAuthMethod() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(underTest.message)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
@@ -202,7 +202,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun patternAuthMethod() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(underTest.message)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Pattern
|
||||
@@ -236,7 +236,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun showOrUnlockDevice_notLocked_switchesToGoneScene() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(true)
|
||||
runCurrent()
|
||||
@@ -249,7 +249,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None)
|
||||
utils.authenticationRepository.setLockscreenEnabled(true)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
@@ -262,7 +262,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun showOrUnlockDevice_customMessageShown() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(underTest.message)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
@@ -283,7 +283,7 @@ class BouncerInteractorTest : SysuiTestCase() {
|
||||
val isThrottled by collectLastValue(underTest.isThrottled)
|
||||
val throttling by collectLastValue(underTest.throttling)
|
||||
val message by collectLastValue(underTest.message)
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
runCurrent()
|
||||
underTest.showOrUnlockDevice()
|
||||
|
||||
@@ -73,14 +73,15 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onShown() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val password by collectLastValue(underTest.password)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
|
||||
underTest.onShown()
|
||||
@@ -93,14 +94,15 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onPasswordInputChanged() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val password by collectLastValue(underTest.password)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
runCurrent()
|
||||
@@ -115,12 +117,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAuthenticateKeyPressed_whenCorrect() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onPasswordInputChanged("password")
|
||||
@@ -133,14 +136,15 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAuthenticateKeyPressed_whenWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val password by collectLastValue(underTest.password)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onPasswordInputChanged("wrong")
|
||||
@@ -155,14 +159,15 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAuthenticateKeyPressed_correctAfterWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val password by collectLastValue(underTest.password)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Password
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onPasswordInputChanged("wrong")
|
||||
|
||||
@@ -76,7 +76,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onShown() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val selectedDots by collectLastValue(underTest.selectedDots)
|
||||
val currentDot by collectLastValue(underTest.currentDot)
|
||||
@@ -84,7 +84,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
AuthenticationMethodModel.Pattern
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
|
||||
underTest.onShown()
|
||||
@@ -98,7 +99,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onDragStart() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val selectedDots by collectLastValue(underTest.selectedDots)
|
||||
val currentDot by collectLastValue(underTest.currentDot)
|
||||
@@ -106,7 +107,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
AuthenticationMethodModel.Pattern
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
runCurrent()
|
||||
@@ -122,14 +124,15 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onDragEnd_whenCorrect() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val selectedDots by collectLastValue(underTest.selectedDots)
|
||||
val currentDot by collectLastValue(underTest.currentDot)
|
||||
utils.authenticationRepository.setAuthenticationMethod(
|
||||
AuthenticationMethodModel.Pattern
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onDragStart()
|
||||
@@ -169,7 +172,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onDragEnd_whenWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val selectedDots by collectLastValue(underTest.selectedDots)
|
||||
val currentDot by collectLastValue(underTest.currentDot)
|
||||
@@ -177,7 +180,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
AuthenticationMethodModel.Pattern
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onDragStart()
|
||||
@@ -201,7 +205,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onDragEnd_correctAfterWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val selectedDots by collectLastValue(underTest.selectedDots)
|
||||
val currentDot by collectLastValue(underTest.currentDot)
|
||||
@@ -209,7 +213,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
|
||||
AuthenticationMethodModel.Pattern
|
||||
)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onDragStart()
|
||||
|
||||
@@ -76,11 +76,13 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onShown() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
|
||||
underTest.onShown()
|
||||
@@ -93,12 +95,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onPinButtonClicked() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
runCurrent()
|
||||
@@ -113,12 +117,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onBackspaceButtonClicked() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
runCurrent()
|
||||
@@ -135,11 +141,13 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onPinEdit() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
|
||||
@@ -157,12 +165,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onBackspaceButtonLongPressed() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
runCurrent()
|
||||
@@ -181,10 +191,12 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAuthenticateButtonClicked_whenCorrect() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
|
||||
@@ -199,12 +211,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAuthenticateButtonClicked_whenWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onPinButtonClicked(1)
|
||||
@@ -223,12 +237,14 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAuthenticateButtonClicked_correctAfterWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
underTest.onPinButtonClicked(1)
|
||||
@@ -255,11 +271,13 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAutoConfirm_whenCorrect() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
utils.authenticationRepository.setAutoConfirmEnabled(true)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit ->
|
||||
@@ -272,13 +290,15 @@ class PinBouncerViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onAutoConfirm_whenWrong() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
val message by collectLastValue(bouncerViewModel.message)
|
||||
val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
utils.authenticationRepository.setAutoConfirmEnabled(true)
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Bouncer), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer), "reason")
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
|
||||
underTest.onShown()
|
||||
FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit ->
|
||||
|
||||
@@ -107,7 +107,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
runCurrent()
|
||||
@@ -120,7 +120,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onContentClicked_deviceUnlocked_switchesToGone() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(true)
|
||||
runCurrent()
|
||||
@@ -133,7 +133,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
runCurrent()
|
||||
@@ -146,7 +146,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onLockButtonClicked_deviceUnlocked_switchesToGone() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(true)
|
||||
runCurrent()
|
||||
|
||||
@@ -56,7 +56,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onContentClicked_deviceUnlocked_switchesToGone() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(true)
|
||||
runCurrent()
|
||||
@@ -69,7 +69,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
runCurrent()
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.android.systemui.scene.SceneTestUtils
|
||||
import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.scene.shared.model.SceneTransitionModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -39,6 +38,7 @@ import org.junit.runners.JUnit4
|
||||
class SceneContainerRepositoryTest : SysuiTestCase() {
|
||||
|
||||
private val utils = SceneTestUtils(this)
|
||||
private val testScope = utils.testScope
|
||||
|
||||
@Test
|
||||
fun allSceneKeys() {
|
||||
@@ -56,97 +56,82 @@ class SceneContainerRepositoryTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun currentScene() = runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val currentScene by collectLastValue(underTest.currentScene)
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
|
||||
fun desiredScene() =
|
||||
testScope.runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val currentScene by collectLastValue(underTest.desiredScene)
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
|
||||
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.Shade))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
underTest.setDesiredScene(SceneModel(SceneKey.Shade))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun setCurrentScene_noSuchSceneInContainer_throws() {
|
||||
fun setDesiredScene_noSuchSceneInContainer_throws() {
|
||||
val underTest =
|
||||
utils.fakeSceneContainerRepository(
|
||||
utils.fakeSceneContainerConfig(listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)),
|
||||
)
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.Shade))
|
||||
underTest.setDesiredScene(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible() = runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val isVisible by collectLastValue(underTest.isVisible)
|
||||
assertThat(isVisible).isTrue()
|
||||
fun isVisible() =
|
||||
testScope.runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val isVisible by collectLastValue(underTest.isVisible)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.setVisible(false)
|
||||
assertThat(isVisible).isFalse()
|
||||
underTest.setVisible(false)
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
underTest.setVisible(true)
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
underTest.setVisible(true)
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transitionProgress() = runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val sceneTransitionProgress by collectLastValue(underTest.transitionProgress)
|
||||
assertThat(sceneTransitionProgress).isEqualTo(1f)
|
||||
fun transitionState_defaultsToIdle() =
|
||||
testScope.runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val transitionState by collectLastValue(underTest.transitionState)
|
||||
|
||||
val transitionState =
|
||||
MutableStateFlow<ObservableTransitionState>(
|
||||
ObservableTransitionState.Idle(SceneKey.Lockscreen)
|
||||
)
|
||||
underTest.setTransitionState(transitionState)
|
||||
assertThat(sceneTransitionProgress).isEqualTo(1f)
|
||||
|
||||
val progress = MutableStateFlow(1f)
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Lockscreen,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = progress,
|
||||
)
|
||||
assertThat(sceneTransitionProgress).isEqualTo(1f)
|
||||
|
||||
progress.value = 0.1f
|
||||
assertThat(sceneTransitionProgress).isEqualTo(0.1f)
|
||||
|
||||
progress.value = 0.9f
|
||||
assertThat(sceneTransitionProgress).isEqualTo(0.9f)
|
||||
|
||||
underTest.setTransitionState(null)
|
||||
assertThat(sceneTransitionProgress).isEqualTo(1f)
|
||||
}
|
||||
assertThat(transitionState)
|
||||
.isEqualTo(
|
||||
ObservableTransitionState.Idle(utils.fakeSceneContainerConfig().initialSceneKey)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setSceneTransition() = runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val sceneTransition by collectLastValue(underTest.transitions)
|
||||
assertThat(sceneTransition).isNull()
|
||||
fun transitionState_reflectsUpdates() =
|
||||
testScope.runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val transitionState =
|
||||
MutableStateFlow<ObservableTransitionState>(
|
||||
ObservableTransitionState.Idle(SceneKey.Lockscreen)
|
||||
)
|
||||
underTest.setTransitionState(transitionState)
|
||||
val reflectedTransitionState by collectLastValue(underTest.transitionState)
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
underTest.setSceneTransition(SceneKey.Lockscreen, SceneKey.QuickSettings)
|
||||
assertThat(sceneTransition)
|
||||
.isEqualTo(
|
||||
SceneTransitionModel(from = SceneKey.Lockscreen, to = SceneKey.QuickSettings)
|
||||
)
|
||||
}
|
||||
val progress = MutableStateFlow(1f)
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Lockscreen,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = progress,
|
||||
)
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun setSceneTransition_noFromSceneInContainer_throws() {
|
||||
val underTest =
|
||||
utils.fakeSceneContainerRepository(
|
||||
utils.fakeSceneContainerConfig(listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)),
|
||||
)
|
||||
underTest.setSceneTransition(SceneKey.Shade, SceneKey.Lockscreen)
|
||||
}
|
||||
progress.value = 0.1f
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun setSceneTransition_noToSceneInContainer_throws() {
|
||||
val underTest =
|
||||
utils.fakeSceneContainerRepository(
|
||||
utils.fakeSceneContainerConfig(listOf(SceneKey.QuickSettings, SceneKey.Lockscreen)),
|
||||
)
|
||||
underTest.setSceneTransition(SceneKey.Shade, SceneKey.Lockscreen)
|
||||
}
|
||||
progress.value = 0.9f
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
underTest.setTransitionState(null)
|
||||
assertThat(reflectedTransitionState)
|
||||
.isEqualTo(
|
||||
ObservableTransitionState.Idle(utils.fakeSceneContainerConfig().initialSceneKey)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,10 +25,12 @@ import com.android.systemui.scene.SceneTestUtils
|
||||
import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.scene.shared.model.SceneTransitionModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
@@ -39,6 +41,7 @@ import org.junit.runners.JUnit4
|
||||
class SceneInteractorTest : SysuiTestCase() {
|
||||
|
||||
private val utils = SceneTestUtils(this)
|
||||
private val testScope = utils.testScope
|
||||
private val repository = utils.fakeSceneContainerRepository()
|
||||
private val underTest = utils.sceneInteractor(repository = repository)
|
||||
|
||||
@@ -48,77 +51,156 @@ class SceneInteractorTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun currentScene() = runTest {
|
||||
val currentScene by collectLastValue(underTest.currentScene)
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
|
||||
fun changeScene() =
|
||||
testScope.runTest {
|
||||
val desiredScene by collectLastValue(underTest.desiredScene)
|
||||
assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
|
||||
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
underTest.changeScene(SceneModel(SceneKey.Shade), "reason")
|
||||
assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sceneTransitionProgress() = runTest {
|
||||
val transitionProgress by collectLastValue(underTest.transitionProgress)
|
||||
assertThat(transitionProgress).isEqualTo(1f)
|
||||
fun onSceneChanged() =
|
||||
testScope.runTest {
|
||||
val desiredScene by collectLastValue(underTest.desiredScene)
|
||||
assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
|
||||
|
||||
val progress = MutableStateFlow(0.55f)
|
||||
repository.setTransitionState(
|
||||
MutableStateFlow(
|
||||
underTest.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
|
||||
assertThat(desiredScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transitionState() =
|
||||
testScope.runTest {
|
||||
val underTest = utils.fakeSceneContainerRepository()
|
||||
val transitionState =
|
||||
MutableStateFlow<ObservableTransitionState>(
|
||||
ObservableTransitionState.Idle(SceneKey.Lockscreen)
|
||||
)
|
||||
underTest.setTransitionState(transitionState)
|
||||
val reflectedTransitionState by collectLastValue(underTest.transitionState)
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
val progress = MutableStateFlow(1f)
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Lockscreen,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = progress,
|
||||
),
|
||||
)
|
||||
)
|
||||
assertThat(transitionProgress).isEqualTo(0.55f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible() = runTest {
|
||||
val isVisible by collectLastValue(underTest.isVisible)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.setVisible(false, "reason")
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
underTest.setVisible(true, "reason")
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sceneTransitions() = runTest {
|
||||
val transitions by collectLastValue(underTest.transitions)
|
||||
assertThat(transitions).isNull()
|
||||
|
||||
val initialSceneKey = underTest.currentScene.value.key
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
|
||||
assertThat(transitions)
|
||||
.isEqualTo(
|
||||
SceneTransitionModel(
|
||||
from = initialSceneKey,
|
||||
to = SceneKey.Shade,
|
||||
)
|
||||
)
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.QuickSettings), "reason")
|
||||
assertThat(transitions)
|
||||
.isEqualTo(
|
||||
SceneTransitionModel(
|
||||
from = SceneKey.Shade,
|
||||
to = SceneKey.QuickSettings,
|
||||
progress.value = 0.1f
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
progress.value = 0.9f
|
||||
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
|
||||
|
||||
underTest.setTransitionState(null)
|
||||
assertThat(reflectedTransitionState)
|
||||
.isEqualTo(
|
||||
ObservableTransitionState.Idle(utils.fakeSceneContainerConfig().initialSceneKey)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun remoteUserInput() = runTest {
|
||||
val remoteUserInput by collectLastValue(underTest.remoteUserInput)
|
||||
assertThat(remoteUserInput).isNull()
|
||||
|
||||
for (input in SceneTestUtils.REMOTE_INPUT_DOWN_GESTURE) {
|
||||
underTest.onRemoteUserInput(input)
|
||||
assertThat(remoteUserInput).isEqualTo(input)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible() =
|
||||
testScope.runTest {
|
||||
val isVisible by collectLastValue(underTest.isVisible)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.setVisible(false, "reason")
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
underTest.setVisible(true, "reason")
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun finishedSceneTransitions() =
|
||||
testScope.runTest {
|
||||
val transitionState =
|
||||
MutableStateFlow<ObservableTransitionState>(
|
||||
ObservableTransitionState.Idle(SceneKey.Lockscreen)
|
||||
)
|
||||
underTest.setTransitionState(transitionState)
|
||||
var transitionCount = 0
|
||||
val job = launch {
|
||||
underTest
|
||||
.finishedSceneTransitions(
|
||||
from = SceneKey.Shade,
|
||||
to = SceneKey.QuickSettings,
|
||||
)
|
||||
.collect { transitionCount++ }
|
||||
}
|
||||
|
||||
assertThat(transitionCount).isEqualTo(0)
|
||||
|
||||
underTest.changeScene(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Lockscreen,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
runCurrent()
|
||||
underTest.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionState.value = ObservableTransitionState.Idle(SceneKey.Shade)
|
||||
runCurrent()
|
||||
assertThat(transitionCount).isEqualTo(0)
|
||||
|
||||
underTest.changeScene(SceneModel(SceneKey.QuickSettings), "reason")
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Shade,
|
||||
toScene = SceneKey.QuickSettings,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
runCurrent()
|
||||
underTest.onSceneChanged(SceneModel(SceneKey.QuickSettings), "reason")
|
||||
transitionState.value = ObservableTransitionState.Idle(SceneKey.QuickSettings)
|
||||
runCurrent()
|
||||
assertThat(transitionCount).isEqualTo(1)
|
||||
|
||||
underTest.changeScene(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.QuickSettings,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
runCurrent()
|
||||
underTest.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionState.value = ObservableTransitionState.Idle(SceneKey.Shade)
|
||||
runCurrent()
|
||||
assertThat(transitionCount).isEqualTo(1)
|
||||
|
||||
underTest.changeScene(SceneModel(SceneKey.QuickSettings), "reason")
|
||||
transitionState.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Shade,
|
||||
toScene = SceneKey.QuickSettings,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
runCurrent()
|
||||
underTest.onSceneChanged(SceneModel(SceneKey.QuickSettings), "reason")
|
||||
transitionState.value = ObservableTransitionState.Idle(SceneKey.QuickSettings)
|
||||
runCurrent()
|
||||
assertThat(transitionCount).isEqualTo(2)
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun remoteUserInput() =
|
||||
testScope.runTest {
|
||||
val remoteUserInput by collectLastValue(underTest.remoteUserInput)
|
||||
assertThat(remoteUserInput).isNull()
|
||||
|
||||
for (input in SceneTestUtils.REMOTE_INPUT_DOWN_GESTURE) {
|
||||
underTest.onRemoteUserInput(input)
|
||||
assertThat(remoteUserInput).isEqualTo(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,15 +29,17 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel
|
||||
import com.android.systemui.keyguard.shared.model.WakefulnessState
|
||||
import com.android.systemui.model.SysUiState
|
||||
import com.android.systemui.scene.SceneTestUtils
|
||||
import com.android.systemui.scene.shared.model.ObservableTransitionState
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
@@ -77,59 +79,86 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
sceneLogger = mock(),
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
prepareState()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hydrateVisibility_featureEnabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentDesiredSceneKey by
|
||||
collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
val isVisible by collectLastValue(sceneInteractor.isVisible)
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isDeviceUnlocked = true,
|
||||
initialSceneKey = SceneKey.Gone,
|
||||
)
|
||||
assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
|
||||
val transitionStateFlow =
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isDeviceUnlocked = true,
|
||||
initialSceneKey = SceneKey.Gone,
|
||||
)
|
||||
assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.start()
|
||||
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Gone,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
assertThat(isVisible).isTrue()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Gone), "reason")
|
||||
transitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Shade,
|
||||
toScene = SceneKey.Gone,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
assertThat(isVisible).isTrue()
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
|
||||
transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Gone)
|
||||
assertThat(isVisible).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hydrateVisibility_featureDisabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentDesiredSceneKey by
|
||||
collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
val isVisible by collectLastValue(sceneInteractor.isVisible)
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
isDeviceUnlocked = true,
|
||||
initialSceneKey = SceneKey.Lockscreen,
|
||||
)
|
||||
assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
|
||||
val transitionStateFlow =
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
isDeviceUnlocked = true,
|
||||
initialSceneKey = SceneKey.Gone,
|
||||
)
|
||||
assertThat(currentDesiredSceneKey).isEqualTo(SceneKey.Gone)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.start()
|
||||
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Gone), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionStateFlow.value =
|
||||
ObservableTransitionState.Transition(
|
||||
fromScene = SceneKey.Gone,
|
||||
toScene = SceneKey.Shade,
|
||||
progress = flowOf(0.5f),
|
||||
)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
sceneInteractor.setCurrentScene(SceneModel(SceneKey.Shade), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
|
||||
transitionStateFlow.value = ObservableTransitionState.Idle(SceneKey.Shade)
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun switchToLockscreenWhenDeviceLocks_featureEnabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isDeviceUnlocked = true,
|
||||
@@ -146,7 +175,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchToLockscreenWhenDeviceLocks_featureDisabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
isDeviceUnlocked = false,
|
||||
@@ -163,7 +192,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isDeviceUnlocked = false,
|
||||
@@ -180,7 +209,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
isDeviceUnlocked = false,
|
||||
@@ -197,7 +226,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isBypassEnabled = true,
|
||||
@@ -214,7 +243,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isBypassEnabled = false,
|
||||
@@ -231,7 +260,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
isBypassEnabled = true,
|
||||
@@ -248,7 +277,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
isDeviceUnlocked = false,
|
||||
@@ -265,7 +294,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
isDeviceUnlocked = false,
|
||||
@@ -282,6 +311,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun hydrateSystemUiState() =
|
||||
testScope.runTest {
|
||||
val transitionStateFlow = prepareState()
|
||||
underTest.start()
|
||||
runCurrent()
|
||||
clearInvocations(sysUiState)
|
||||
@@ -294,9 +324,16 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
SceneKey.QuickSettings,
|
||||
)
|
||||
.forEachIndexed { index, sceneKey ->
|
||||
sceneInteractor.setCurrentScene(SceneModel(sceneKey), "reason")
|
||||
sceneInteractor.changeScene(SceneModel(sceneKey), "reason")
|
||||
runCurrent()
|
||||
verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY)
|
||||
|
||||
sceneInteractor.onSceneChanged(SceneModel(sceneKey), "reason")
|
||||
runCurrent()
|
||||
verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY)
|
||||
|
||||
transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey)
|
||||
runCurrent()
|
||||
verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY)
|
||||
}
|
||||
}
|
||||
@@ -304,7 +341,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureEnabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
initialSceneKey = SceneKey.Lockscreen,
|
||||
@@ -321,7 +358,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNotNone_featureEnabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = true,
|
||||
initialSceneKey = SceneKey.Lockscreen,
|
||||
@@ -338,7 +375,7 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun switchToGoneWhenDeviceStartsToWakeUp_authMethodNone_featureDisabled() =
|
||||
testScope.runTest {
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.currentScene.map { it.key })
|
||||
val currentSceneKey by collectLastValue(sceneInteractor.desiredScene.map { it.key })
|
||||
prepareState(
|
||||
isFeatureEnabled = false,
|
||||
initialSceneKey = SceneKey.Lockscreen,
|
||||
@@ -358,17 +395,27 @@ class SceneContainerStartableTest : SysuiTestCase() {
|
||||
isBypassEnabled: Boolean = false,
|
||||
initialSceneKey: SceneKey? = null,
|
||||
authenticationMethod: AuthenticationMethodModel? = null,
|
||||
) {
|
||||
): MutableStateFlow<ObservableTransitionState> {
|
||||
featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled)
|
||||
authenticationRepository.setUnlocked(isDeviceUnlocked)
|
||||
keyguardRepository.setBypassEnabled(isBypassEnabled)
|
||||
initialSceneKey?.let { sceneInteractor.setCurrentScene(SceneModel(it), "reason") }
|
||||
val transitionStateFlow =
|
||||
MutableStateFlow<ObservableTransitionState>(
|
||||
ObservableTransitionState.Idle(SceneKey.Lockscreen)
|
||||
)
|
||||
sceneInteractor.setTransitionState(transitionStateFlow)
|
||||
initialSceneKey?.let {
|
||||
transitionStateFlow.value = ObservableTransitionState.Idle(it)
|
||||
sceneInteractor.changeScene(SceneModel(it), "reason")
|
||||
sceneInteractor.onSceneChanged(SceneModel(it), "reason")
|
||||
}
|
||||
authenticationMethod?.let {
|
||||
authenticationRepository.setAuthenticationMethod(authenticationMethod)
|
||||
authenticationRepository.setLockscreenEnabled(
|
||||
authenticationMethod != AuthenticationMethodModel.None
|
||||
)
|
||||
}
|
||||
return transitionStateFlow
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -69,7 +69,8 @@ class SceneContainerViewModelTest : SysuiTestCase() {
|
||||
val currentScene by collectLastValue(underTest.currentScene)
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
|
||||
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.Shade))
|
||||
underTest.onSceneChanged(SceneModel(SceneKey.Shade))
|
||||
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onContentClicked_deviceUnlocked_switchesToGone() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(true)
|
||||
runCurrent()
|
||||
@@ -91,7 +91,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
|
||||
testScope.runTest {
|
||||
val currentScene by collectLastValue(sceneInteractor.currentScene)
|
||||
val currentScene by collectLastValue(sceneInteractor.desiredScene)
|
||||
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
|
||||
utils.authenticationRepository.setUnlocked(false)
|
||||
runCurrent()
|
||||
|
||||
@@ -97,7 +97,7 @@ class SceneTestUtils(
|
||||
fun fakeSceneContainerRepository(
|
||||
containerConfig: SceneContainerConfig = fakeSceneContainerConfig(),
|
||||
): SceneContainerRepository {
|
||||
return SceneContainerRepository(containerConfig)
|
||||
return SceneContainerRepository(applicationScope(), containerConfig)
|
||||
}
|
||||
|
||||
fun fakeSceneKeys(): List<SceneKey> {
|
||||
|
||||
Reference in New Issue
Block a user