Transitions - Add ALTERNATE_BOUNCER support

* Add KeyguardState.ALTERNATE_BOUNCER and distinguish this
from the KeyguardState.PRIMARY_BOUNCER
* Remove bouncer state from the KeyguardRepository; instead
rely on the bouncer visibility states from KeyguardBouncerRepository

Bug: 268240415
Test: manually check KeyguardTransitionAuditLogger logs
Test: atest KeyguardInteractorTest KeyguardTransitionScenariosTest
Change-Id: I130647913e5344e76e9e047f619022e7a9f1498e
This commit is contained in:
Beverly
2023-02-08 13:55:47 +00:00
parent 6116f0a6ae
commit 4153b90fa6
38 changed files with 698 additions and 176 deletions

View File

@@ -933,7 +933,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
}
// The smartspace is not visible if the bouncer is showing, so don't shared element it.
if (keyguardStateController.isBouncerShowing) {
if (keyguardStateController.isPrimaryBouncerShowing) {
return false
}

View File

@@ -1148,12 +1148,12 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable,
private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
new KeyguardStateController.Callback() {
@Override
public void onBouncerShowingChanged() {
public void onPrimaryBouncerShowingChanged() {
synchronized (KeyguardViewMediator.this) {
if (mKeyguardStateController.isBouncerShowing()) {
if (mKeyguardStateController.isPrimaryBouncerShowing()) {
mPendingPinLock = false;
}
adjustStatusBarLocked(mKeyguardStateController.isBouncerShowing(), false);
adjustStatusBarLocked(mKeyguardStateController.isPrimaryBouncerShowing(), false);
}
}
};

View File

@@ -68,8 +68,8 @@ interface KeyguardBouncerRepository {
val resourceUpdateRequests: StateFlow<Boolean>
val bouncerPromptReason: Int
val bouncerErrorMessage: CharSequence?
val isAlternateBouncerVisible: StateFlow<Boolean>
val isAlternateBouncerUIAvailable: StateFlow<Boolean>
val alternateBouncerVisible: StateFlow<Boolean>
val alternateBouncerUIAvailable: StateFlow<Boolean>
var lastAlternateBouncerVisibleTime: Long
fun setPrimaryScrimmed(isScrimmed: Boolean)
@@ -159,12 +159,12 @@ constructor(
get() = viewMediatorCallback.consumeCustomMessage()
/** Values associated with the AlternateBouncer */
private val _isAlternateBouncerVisible = MutableStateFlow(false)
override val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
private val _alternateBouncerVisible = MutableStateFlow(false)
override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
private val _isAlternateBouncerUIAvailable = MutableStateFlow(false)
override val isAlternateBouncerUIAvailable: StateFlow<Boolean> =
_isAlternateBouncerUIAvailable.asStateFlow()
private val _alternateBouncerUIAvailable = MutableStateFlow(false)
override val alternateBouncerUIAvailable: StateFlow<Boolean> =
_alternateBouncerUIAvailable.asStateFlow()
init {
setUpLogging()
@@ -179,16 +179,16 @@ constructor(
}
override fun setAlternateVisible(isVisible: Boolean) {
if (isVisible && !_isAlternateBouncerVisible.value) {
if (isVisible && !_alternateBouncerVisible.value) {
lastAlternateBouncerVisibleTime = clock.uptimeMillis()
} else if (!isVisible) {
lastAlternateBouncerVisibleTime = NOT_VISIBLE
}
_isAlternateBouncerVisible.value = isVisible
_alternateBouncerVisible.value = isVisible
}
override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
_isAlternateBouncerUIAvailable.value = isAvailable
_alternateBouncerUIAvailable.value = isAvailable
}
override fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
@@ -290,7 +290,7 @@ constructor(
resourceUpdateRequests
.logDiffsForTable(buffer, "", "ResourceUpdateRequests", false)
.launchIn(applicationScope)
isAlternateBouncerUIAvailable
alternateBouncerUIAvailable
.logDiffsForTable(buffer, "", "IsAlternateBouncerUIAvailable", false)
.launchIn(applicationScope)
}

View File

@@ -86,9 +86,6 @@ interface KeyguardRepository {
/** Observable for the signal that keyguard is about to go away. */
val isKeyguardGoingAway: Flow<Boolean>
/** Observable for whether the bouncer is showing. */
val isBouncerShowing: Flow<Boolean>
/** Is the always-on display available to be used? */
val isAodAvailable: Flow<Boolean>
@@ -304,29 +301,6 @@ constructor(
awaitClose { keyguardStateController.removeCallback(callback) }
}
override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
override fun onBouncerShowingChanged() {
trySendWithFailureLogging(
keyguardStateController.isBouncerShowing,
TAG,
"updated isBouncerShowing"
)
}
}
keyguardStateController.addCallback(callback)
// Adding the callback does not send an initial update.
trySendWithFailureLogging(
keyguardStateController.isBouncerShowing,
TAG,
"initial isBouncerShowing"
)
awaitClose { keyguardStateController.removeCallback(callback) }
}
override val isDozing: Flow<Boolean> =
conflatedCallbackFlow {
val callback =

View File

@@ -44,7 +44,7 @@ constructor(
var legacyAlternateBouncer: LegacyAlternateBouncer? = null
var legacyAlternateBouncerVisibleTime: Long = NOT_VISIBLE
val isVisible: Flow<Boolean> = bouncerRepository.isAlternateBouncerVisible
val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
/**
* Sets the correct bouncer states to show the alternate bouncer if it can show.
@@ -86,7 +86,7 @@ constructor(
fun isVisibleState(): Boolean {
return if (isModernAlternateBouncerEnabled) {
bouncerRepository.isAlternateBouncerVisible.value
bouncerRepository.alternateBouncerVisible.value
} else {
legacyAlternateBouncer?.isShowingAlternateBouncer ?: false
}
@@ -98,7 +98,7 @@ constructor(
fun canShowAlternateBouncerForFingerprint(): Boolean {
return if (isModernAlternateBouncerEnabled) {
bouncerRepository.isAlternateBouncerUIAvailable.value &&
bouncerRepository.alternateBouncerUIAvailable.value &&
biometricSettingsRepository.isFingerprintEnrolled.value &&
biometricSettingsRepository.isStrongBiometricAllowed.value &&
biometricSettingsRepository.isFingerprintEnabledByDevicePolicy.value &&

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 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.keyguard.domain.interactor
import android.animation.ValueAnimator
import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
class FromAlternateBouncerTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor(FromAlternateBouncerTransitionInteractor::class.simpleName!!) {
override fun start() {
listenForAlternateBouncerToGone()
listenForAlternateBouncerToLockscreenAodOrDozing()
listenForAlternateBouncerToPrimaryBouncer()
}
private fun listenForAlternateBouncerToLockscreenAodOrDozing() {
scope.launch {
keyguardInteractor.alternateBouncerShowing
// Add a slight delay, as alternateBouncer and primaryBouncer showing event changes
// will arrive with a small gap in time. This prevents a transition to LOCKSCREEN
// happening prematurely.
.onEach { delay(50) }
.sample(
combine(
keyguardInteractor.primaryBouncerShowing,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.wakefulnessModel,
keyguardInteractor.isAodAvailable,
::toQuad
),
::toQuint
)
.collect {
(
isAlternateBouncerShowing,
isPrimaryBouncerShowing,
lastStartedTransitionStep,
wakefulnessState,
isAodAvailable
) ->
if (
!isAlternateBouncerShowing &&
!isPrimaryBouncerShowing &&
lastStartedTransitionStep.to == KeyguardState.ALTERNATE_BOUNCER
) {
val to =
if (
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
wakefulnessState.state == WakefulnessState.ASLEEP
) {
if (isAodAvailable) {
KeyguardState.AOD
} else {
KeyguardState.DOZING
}
} else {
KeyguardState.LOCKSCREEN
}
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.ALTERNATE_BOUNCER,
to = to,
animator = getAnimator(),
)
)
}
}
}
}
private fun listenForAlternateBouncerToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
.sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
.collect { (isKeyguardGoingAway, keyguardState) ->
if (isKeyguardGoingAway && keyguardState == KeyguardState.ALTERNATE_BOUNCER) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.ALTERNATE_BOUNCER,
to = KeyguardState.GONE,
animator = getAnimator(),
)
)
}
}
}
}
private fun listenForAlternateBouncerToPrimaryBouncer() {
scope.launch {
keyguardInteractor.primaryBouncerShowing
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { (isPrimaryBouncerShowing, startedKeyguardState) ->
if (
isPrimaryBouncerShowing &&
startedKeyguardState.to == KeyguardState.ALTERNATE_BOUNCER
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.ALTERNATE_BOUNCER,
to = KeyguardState.PRIMARY_BOUNCER,
animator = getAnimator(),
)
)
}
}
}
}
private fun getAnimator(): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
duration = TRANSITION_DURATION_MS
}
}
companion object {
private const val TRANSITION_DURATION_MS = 300L
}
}

View File

@@ -33,7 +33,6 @@ import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -53,9 +52,10 @@ constructor(
listenForLockscreenToOccluded()
listenForLockscreenToCamera()
listenForLockscreenToAodOrDozing()
listenForLockscreenToBouncer()
listenForLockscreenToPrimaryBouncer()
listenForLockscreenToDreaming()
listenForLockscreenToBouncerDragging()
listenForLockscreenToPrimaryBouncerDragging()
listenForLockscreenToAlternateBouncer()
}
private fun listenForLockscreenToDreaming() {
@@ -78,9 +78,9 @@ constructor(
}
}
private fun listenForLockscreenToBouncer() {
private fun listenForLockscreenToPrimaryBouncer() {
scope.launch {
keyguardInteractor.isBouncerShowing
keyguardInteractor.primaryBouncerShowing
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isBouncerShowing, lastStartedTransitionStep) = pair
@@ -91,7 +91,30 @@ constructor(
TransitionInfo(
ownerName = name,
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.BOUNCER,
to = KeyguardState.PRIMARY_BOUNCER,
animator = getAnimator(),
)
)
}
}
}
}
private fun listenForLockscreenToAlternateBouncer() {
scope.launch {
keyguardInteractor.alternateBouncerShowing
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isAlternateBouncerShowing, lastStartedTransitionStep) = pair
if (
isAlternateBouncerShowing &&
lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.ALTERNATE_BOUNCER,
animator = getAnimator(),
)
)
@@ -101,7 +124,7 @@ constructor(
}
/* Starts transitions when manually dragging up the bouncer from the lockscreen. */
private fun listenForLockscreenToBouncerDragging() {
private fun listenForLockscreenToPrimaryBouncerDragging() {
var transitionId: UUID? = null
scope.launch {
shadeRepository.shadeModel
@@ -144,7 +167,7 @@ constructor(
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.BOUNCER,
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.LOCKSCREEN,
animator = getAnimator(0.milliseconds)
)
@@ -163,7 +186,7 @@ constructor(
TransitionInfo(
ownerName = name,
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.BOUNCER,
to = KeyguardState.PRIMARY_BOUNCER,
animator = null,
)
)

View File

@@ -24,62 +24,63 @@ import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@SysUISingleton
class FromBouncerTransitionInteractor
class FromPrimaryBouncerTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
) : TransitionInteractor(FromBouncerTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null
) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
override fun start() {
listenForBouncerToGone()
listenForBouncerToLockscreenOrAod()
listenForPrimaryBouncerToGone()
listenForPrimaryBouncerToLockscreenAodOrDozing()
}
private fun listenForBouncerToLockscreenOrAod() {
private fun listenForPrimaryBouncerToLockscreenAodOrDozing() {
scope.launch {
keyguardInteractor.isBouncerShowing
keyguardInteractor.primaryBouncerShowing
.sample(
combine(
keyguardInteractor.wakefulnessModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
::Pair
keyguardInteractor.isAodAvailable,
::toTriple
),
::toTriple
::toQuad
)
.collect { triple ->
val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
.collect {
(isBouncerShowing, wakefulnessState, lastStartedTransitionStep, isAodAvailable)
->
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
!isBouncerShowing &&
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
) {
val to =
if (
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
wakefulnessState.state == WakefulnessState.ASLEEP
) {
KeyguardState.AOD
if (isAodAvailable) {
KeyguardState.AOD
} else {
KeyguardState.DOZING
}
} else {
KeyguardState.LOCKSCREEN
}
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.BOUNCER,
from = KeyguardState.PRIMARY_BOUNCER,
to = to,
animator = getAnimator(),
)
@@ -89,17 +90,17 @@ constructor(
}
}
private fun listenForBouncerToGone() {
private fun listenForPrimaryBouncerToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) }
.collect { pair ->
val (isKeyguardGoingAway, keyguardState) = pair
if (isKeyguardGoingAway && keyguardState == KeyguardState.BOUNCER) {
if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.BOUNCER,
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE,
animator = getAnimator(),
)

View File

@@ -24,6 +24,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -56,6 +57,7 @@ constructor(
private val repository: KeyguardRepository,
private val commandQueue: CommandQueue,
featureFlags: FeatureFlags,
bouncerRepository: KeyguardBouncerRepository,
) {
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -121,8 +123,10 @@ constructor(
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** Whether the primary bouncer is showing or not. */
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
/** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
/** The device wake/sleep state */
val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */
@@ -142,12 +146,12 @@ constructor(
if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
combine(
isKeyguardVisible,
repository.isBouncerShowing,
bouncerRepository.primaryBouncerVisible,
onCameraLaunchDetected,
) { isKeyguardVisible, isBouncerShowing, cameraLaunchEvent ->
) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent ->
when {
isKeyguardVisible -> false
isBouncerShowing -> false
isPrimaryBouncerShowing -> false
else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
}
}

View File

@@ -22,7 +22,6 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.plugins.log.LogLevel.VERBOSE
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
private val TAG = KeyguardTransitionAuditLogger::class.simpleName!!
@@ -46,8 +45,14 @@ constructor(
}
scope.launch {
keyguardInteractor.isBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Bouncer showing", it)
keyguardInteractor.primaryBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Primary bouncer showing", it)
}
}
scope.launch {
keyguardInteractor.alternateBouncerShowing.collect {
logger.log(TAG, VERBOSE, "Alternate bouncer showing", it)
}
}

View File

@@ -37,13 +37,14 @@ constructor(
// exhaustive
val ret =
when (it) {
is FromBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
is FromPrimaryBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
is FromAodTransitionInteractor -> Log.d(TAG, "Started $it")
is FromGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is FromLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
is FromDreamingTransitionInteractor -> Log.d(TAG, "Started $it")
is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
is FromAlternateBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}

View File

@@ -21,13 +21,12 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -63,9 +62,9 @@ constructor(
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
/** LOCKSCREEN->BOUNCER transition information. */
val lockscreenToBouncerTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, BOUNCER)
/** LOCKSCREEN->PRIMARY_BOUNCER transition information. */
val mLockscreenToPrimaryBouncerTransition: Flow<TransitionStep> =
repository.transition(LOCKSCREEN, PRIMARY_BOUNCER)
/** LOCKSCREEN->DREAMING transition information. */
val lockscreenToDreamingTransition: Flow<TransitionStep> =

View File

@@ -86,7 +86,8 @@ constructor(
KeyguardState.DOZING -> false
KeyguardState.AOD -> false
KeyguardState.DREAMING -> true
KeyguardState.BOUNCER -> true
KeyguardState.ALTERNATE_BOUNCER -> true
KeyguardState.PRIMARY_BOUNCER -> true
KeyguardState.LOCKSCREEN -> true
KeyguardState.GONE -> true
KeyguardState.OCCLUDED -> true

View File

@@ -164,7 +164,7 @@ constructor(
} else {
DejankUtils.postAfterTraversal(showRunnable)
}
keyguardStateController.notifyBouncerShowing(true)
keyguardStateController.notifyPrimaryBouncerShowing(true)
primaryBouncerCallbackInteractor.dispatchStartingToShow()
Trace.endSection()
}
@@ -181,7 +181,7 @@ constructor(
}
falsingCollector.onBouncerHidden()
keyguardStateController.notifyBouncerShowing(false /* showing */)
keyguardStateController.notifyPrimaryBouncerShowing(false /* showing */)
cancelShowRunnable()
repository.setPrimaryShowingSoon(false)
repository.setPrimaryVisible(false)

View File

@@ -32,7 +32,9 @@ abstract class StartKeyguardTransitionModule {
@Binds
@IntoSet
abstract fun fromBouncer(impl: FromBouncerTransitionInteractor): TransitionInteractor
abstract fun fromPrimaryBouncer(
impl: FromPrimaryBouncerTransitionInteractor
): TransitionInteractor
@Binds
@IntoSet
@@ -53,4 +55,10 @@ abstract class StartKeyguardTransitionModule {
@Binds
@IntoSet
abstract fun fromDozing(impl: FromDozingTransitionInteractor): TransitionInteractor
@Binds
@IntoSet
abstract fun fromAlternateBouncer(
impl: FromAlternateBouncerTransitionInteractor
): TransitionInteractor
}

View File

@@ -30,7 +30,26 @@ sealed class TransitionInteractor(val name: String) {
abstract fun start()
fun <A, B, C> toTriple(a: A, b: B, c: C) = Triple(a, b, c)
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
fun <A, B, C, D> toQuad(a: A, b: B, c: C, d: D) = Quad(a, b, c, d)
fun <A, B, C, D> toQuad(a: A, bcd: Triple<B, C, D>) = Quad(a, bcd.first, bcd.second, bcd.third)
fun <A, B, C, D, E> toQuint(a: A, bcde: Quad<B, C, D, E>) =
Quint(a, bcde.first, bcde.second, bcde.third, bcde.fourth)
}
data class Quad<A, B, C, D>(val first: A, val second: B, val third: C, val fourth: D)
data class Quint<A, B, C, D, E>(
val first: A,
val second: B,
val third: C,
val fourth: D,
val fifth: E
)

View File

@@ -42,10 +42,15 @@ enum class KeyguardState {
*/
AOD,
/*
* The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
* (Fingerprint Sensor) variations, for the user to verify their credentials
* The security screen prompt containing UI to prompt the user to use a biometric credential
* (ie: fingerprint). When supported, this may show before showing the primary bouncer.
*/
BOUNCER,
ALTERNATE_BOUNCER,
/*
* The security screen prompt UI, containing PIN, Password, Pattern for the user to verify their
* credentials.
*/
PRIMARY_BOUNCER,
/*
* Device is actively displaying keyguard UI and is not in low-power mode. Device may be
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false.

View File

@@ -56,7 +56,7 @@ public interface KeyguardStateController extends CallbackController<Callback> {
/**
* Whether the bouncer (PIN/password entry) is currently visible.
*/
boolean isBouncerShowing();
boolean isPrimaryBouncerShowing();
/**
* If swiping up will unlock without asking for a password.
@@ -196,7 +196,7 @@ public interface KeyguardStateController extends CallbackController<Callback> {
/** **/
default void notifyKeyguardState(boolean showing, boolean occluded) {}
/** **/
default void notifyBouncerShowing(boolean showing) {}
default void notifyPrimaryBouncerShowing(boolean showing) {}
/**
* Updates the keyguard state to reflect that it's in the process of being dismissed, either by
@@ -244,7 +244,7 @@ public interface KeyguardStateController extends CallbackController<Callback> {
/**
* Called when the bouncer (PIN/password entry) is shown or hidden.
*/
default void onBouncerShowingChanged() {}
default void onPrimaryBouncerShowingChanged() {}
/**
* Triggered when the device was just unlocked and the lock screen is being dismissed.

View File

@@ -65,7 +65,7 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
private boolean mCanDismissLockScreen;
private boolean mShowing;
private boolean mBouncerShowing;
private boolean mPrimaryBouncerShowing;
private boolean mSecure;
private boolean mOccluded;
@@ -157,8 +157,8 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
}
@Override
public boolean isBouncerShowing() {
return mBouncerShowing;
public boolean isPrimaryBouncerShowing() {
return mPrimaryBouncerShowing;
}
@Override
@@ -339,11 +339,11 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum
}
@Override
public void notifyBouncerShowing(boolean showing) {
if (mBouncerShowing != showing) {
mBouncerShowing = showing;
public void notifyPrimaryBouncerShowing(boolean showing) {
if (mPrimaryBouncerShowing != showing) {
mPrimaryBouncerShowing = showing;
new ArrayList<>(mCallbacks).forEach(Callback::onBouncerShowingChanged);
new ArrayList<>(mCallbacks).forEach(Callback::onPrimaryBouncerShowingChanged);
}
}

View File

@@ -23,6 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -85,6 +86,7 @@ class ClockEventControllerTest : SysuiTestCase() {
@Mock private lateinit var transitionRepository: KeyguardTransitionRepository
@Mock private lateinit var commandQueue: CommandQueue
private lateinit var repository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
@Mock private lateinit var smallLogBuffer: LogBuffer
@Mock private lateinit var largeLogBuffer: LogBuffer
private lateinit var underTest: ClockEventController
@@ -103,11 +105,15 @@ class ClockEventControllerTest : SysuiTestCase() {
whenever(largeClockEvents.tickRate).thenReturn(ClockTickRate.PER_MINUTE)
repository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
underTest = ClockEventController(
KeyguardInteractor(repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags),
KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = bouncerRepository,
),
KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher,
batteryController,

View File

@@ -43,6 +43,7 @@ import com.android.systemui.biometrics.AuthRippleController;
import com.android.systemui.doze.util.BurnInHelperKt;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -159,9 +160,12 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mAuthRippleController,
mResources,
new KeyguardTransitionInteractor(mTransitionRepository),
new KeyguardInteractor(new FakeKeyguardRepository(),
new KeyguardInteractor(
new FakeKeyguardRepository(),
mCommandQueue,
mFeatureFlags),
mFeatureFlags,
new FakeKeyguardBouncerRepository()
),
mFeatureFlags
);
}

View File

@@ -39,6 +39,7 @@ import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffor
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -172,6 +173,7 @@ class CustomizationProviderTest : SysuiTestCase() {
repository = FakeKeyguardRepository(),
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
),
registry = mock(),
lockPatternUtils = lockPatternUtils,

View File

@@ -323,29 +323,6 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
assertThat(underTest.isUdfpsSupported()).isFalse()
}
@Test
fun isBouncerShowing() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
captor.value.onBouncerShowingChanged()
assertThat(latest).isTrue()
whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
captor.value.onBouncerShowingChanged()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun isKeyguardGoingAway() =
runTest(UnconfinedTestDispatcher()) {

View File

@@ -130,7 +130,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
givenCanShowAlternateBouncer()
assertTrue(underTest.show())
assertTrue(bouncerRepository.isAlternateBouncerVisible.value)
assertTrue(bouncerRepository.alternateBouncerVisible.value)
}
@Test
@@ -138,7 +138,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
givenCannotShowAlternateBouncer()
assertFalse(underTest.show())
assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
assertFalse(bouncerRepository.alternateBouncerVisible.value)
}
@Test
@@ -146,7 +146,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
bouncerRepository.setAlternateVisible(true)
assertTrue(underTest.hide())
assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
assertFalse(bouncerRepository.alternateBouncerVisible.value)
}
@Test
@@ -154,7 +154,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
bouncerRepository.setAlternateVisible(false)
assertFalse(underTest.hide())
assertFalse(bouncerRepository.isAlternateBouncerVisible.value)
assertFalse(bouncerRepository.alternateBouncerVisible.value)
}
private fun givenCanShowAlternateBouncer() {

View File

@@ -25,6 +25,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
import com.android.systemui.settings.DisplayTracker
@@ -38,7 +39,6 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -50,6 +50,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
private lateinit var underTest: KeyguardInteractor
private lateinit var repository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
@Before
fun setUp() {
@@ -58,7 +59,14 @@ class KeyguardInteractorTest : SysuiTestCase() {
commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
testScope = TestScope()
repository = FakeKeyguardRepository()
underTest = KeyguardInteractor(repository, commandQueue, featureFlags)
bouncerRepository = FakeKeyguardBouncerRepository()
underTest =
KeyguardInteractor(
repository,
commandQueue,
featureFlags,
bouncerRepository,
)
}
@Test
@@ -137,7 +145,7 @@ class KeyguardInteractorTest : SysuiTestCase() {
repository.setKeyguardOccluded(true)
assertThat(secureCameraActive()).isTrue()
repository.setBouncerShowing(true)
bouncerRepository.setPrimaryVisible(true)
assertThat(secureCameraActive()).isFalse()
}

View File

@@ -36,6 +36,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
@@ -298,6 +299,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
repository = FakeKeyguardRepository(),
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
),
registry =
FakeKeyguardQuickAffordanceRegistry(

View File

@@ -36,6 +36,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
@@ -159,7 +160,8 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
),
registry =
FakeKeyguardQuickAffordanceRegistry(

View File

@@ -22,7 +22,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
@@ -65,6 +67,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
private lateinit var testScope: TestScope
private lateinit var keyguardRepository: FakeKeyguardRepository
private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
private lateinit var shadeRepository: ShadeRepository
// Used to issue real transition steps for test input
@@ -81,6 +84,10 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor
private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor
private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor
private lateinit var fromAlternateBouncerTransitionInteractor:
FromAlternateBouncerTransitionInteractor
private lateinit var fromPrimaryBouncerTransitionInteractor:
FromPrimaryBouncerTransitionInteractor
@Before
fun setUp() {
@@ -88,6 +95,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
testScope = TestScope()
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
/* Used to issue full transition steps, to better simulate a real device */
@@ -98,8 +106,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
scope = testScope,
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardInteractor = createKeyguardInteractor(featureFlags),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -109,8 +116,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -119,8 +125,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -129,8 +134,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -139,8 +143,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -149,12 +152,29 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromOccludedTransitionInteractor.start()
fromAlternateBouncerTransitionInteractor =
FromAlternateBouncerTransitionInteractor(
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromAlternateBouncerTransitionInteractor.start()
fromPrimaryBouncerTransitionInteractor =
FromPrimaryBouncerTransitionInteractor(
scope = testScope,
keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromPrimaryBouncerTransitionInteractor.start()
}
@Test
@@ -256,7 +276,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
}
@Test
fun `LOCKSCREEN to BOUNCER via bouncer showing call`() =
fun `LOCKSCREEN to PRIMARY_BOUNCER via bouncer showing call`() =
testScope.runTest {
// GIVEN a device that has at least woken up
keyguardRepository.setWakefulnessModel(startingToWake())
@@ -278,18 +298,18 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
)
runCurrent()
// WHEN the bouncer is set to show
keyguardRepository.setBouncerShowing(true)
// WHEN the primary bouncer is set to show
bouncerRepository.setPrimaryVisible(true)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to BOUNCER should occur
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.to).isEqualTo(KeyguardState.BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
@@ -695,6 +715,297 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
@Test
fun `ALTERNATE_BOUNCER to PRIMARY_BOUNCER`() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.ALTERNATE_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the alternateBouncer stops showing and then the primary bouncer shows
bouncerRepository.setPrimaryVisible(true)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `ALTERNATE_BOUNCER to AOD`() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.ALTERNATE_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// GIVEN the primary bouncer isn't showing, aod available and starting to sleep
bouncerRepository.setPrimaryVisible(false)
keyguardRepository.setAodAvailable(true)
keyguardRepository.setWakefulnessModel(startingToSleep())
// WHEN the alternateBouncer stops showing
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.AOD)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `ALTERNATE_BOUNCER to DOZING`() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.ALTERNATE_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// GIVEN the primary bouncer isn't showing, aod not available and starting to sleep
// to sleep
bouncerRepository.setPrimaryVisible(false)
keyguardRepository.setAodAvailable(false)
keyguardRepository.setWakefulnessModel(startingToSleep())
// WHEN the alternateBouncer stops showing
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.DOZING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `ALTERNATE_BOUNCER to LOCKSCREEN`() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
bouncerRepository.setAlternateVisible(true)
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.ALTERNATE_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// GIVEN the primary bouncer isn't showing and device not sleeping
bouncerRepository.setPrimaryVisible(false)
keyguardRepository.setWakefulnessModel(startingToWake())
// WHEN the alternateBouncer stops showing
bouncerRepository.setAlternateVisible(false)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.ALTERNATE_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `PRIMARY_BOUNCER to AOD`() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryVisible(true)
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// GIVEN aod available and starting to sleep
keyguardRepository.setAodAvailable(true)
keyguardRepository.setWakefulnessModel(startingToSleep())
// WHEN the primaryBouncer stops showing
bouncerRepository.setPrimaryVisible(false)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.AOD)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `PRIMARY_BOUNCER to DOZING`() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryVisible(true)
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// GIVEN aod not available and starting to sleep to sleep
keyguardRepository.setAodAvailable(false)
keyguardRepository.setWakefulnessModel(startingToSleep())
// WHEN the primaryBouncer stops showing
bouncerRepository.setPrimaryVisible(false)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.DOZING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `PRIMARY_BOUNCER to LOCKSCREEN`() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
bouncerRepository.setPrimaryVisible(true)
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.PRIMARY_BOUNCER,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// GIVEN device not sleeping
keyguardRepository.setWakefulnessModel(startingToWake())
// WHEN the alternateBouncer stops showing
bouncerRepository.setPrimaryVisible(false)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.PRIMARY_BOUNCER)
assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
private fun startingToWake() =
WakefulnessModel(
WakefulnessState.STARTING_TO_WAKE,
@@ -710,4 +1021,13 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
WakeSleepReason.OTHER,
WakeSleepReason.OTHER
)
private fun createKeyguardInteractor(featureFlags: FeatureFlags): KeyguardInteractor {
return KeyguardInteractor(
keyguardRepository,
commandQueue,
featureFlags,
bouncerRepository,
)
}
}

View File

@@ -102,7 +102,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
verify(repository).setPrimaryScrimmed(true)
verify(repository).setPanelExpansion(EXPANSION_VISIBLE)
verify(repository).setPrimaryShowingSoon(true)
verify(keyguardStateController).notifyBouncerShowing(true)
verify(keyguardStateController).notifyPrimaryBouncerShowing(true)
verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToShow()
verify(repository).setPrimaryVisible(true)
verify(repository).setPrimaryShow(any(KeyguardBouncerModel::class.java))
@@ -118,7 +118,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testShow_keyguardIsDone() {
`when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
verify(keyguardStateController, never()).notifyBouncerShowing(true)
verify(keyguardStateController, never()).notifyPrimaryBouncerShowing(true)
verify(mPrimaryBouncerCallbackInteractor, never()).dispatchStartingToShow()
}
@@ -126,7 +126,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
fun testHide() {
underTest.hide()
verify(falsingCollector).onBouncerHidden()
verify(keyguardStateController).notifyBouncerShowing(false)
verify(keyguardStateController).notifyPrimaryBouncerShowing(false)
verify(repository).setPrimaryShowingSoon(false)
verify(repository).setPrimaryVisible(false)
verify(repository).setPrimaryHide(true)

View File

@@ -35,6 +35,7 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLegacySettingSyncer
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceLocalUserSelectionManager
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
@@ -136,6 +137,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
)
whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))

View File

@@ -53,7 +53,7 @@ public class FakeKeyguardStateController implements KeyguardStateController {
}
@Override
public boolean isBouncerShowing() {
public boolean isPrimaryBouncerShowing() {
return false;
}

View File

@@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.shade.NotificationPanelViewController
@@ -112,7 +113,8 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
featureFlags = featureFlags
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
)
// Needs to be run on the main thread

View File

@@ -39,6 +39,7 @@ import com.android.systemui.common.shared.model.Text
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
@@ -143,6 +144,7 @@ class UserInteractorTest : SysuiTestCase() {
repository = keyguardRepository,
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
),
manager = manager,
applicationScope = testScope.backgroundScope,

View File

@@ -31,6 +31,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
@@ -249,6 +250,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
repository = keyguardRepository,
commandQueue = commandQueue,
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
),
featureFlags = featureFlags,
manager = manager,

View File

@@ -29,6 +29,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Text
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
@@ -150,7 +151,8 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
featureFlags = featureFlags
featureFlags = featureFlags,
bouncerRepository = FakeKeyguardBouncerRepository(),
),
featureFlags = featureFlags,
manager = manager,

View File

@@ -57,10 +57,10 @@ class FakeKeyguardBouncerRepository : KeyguardBouncerRepository {
override val bouncerPromptReason = 0
override val bouncerErrorMessage: CharSequence? = null
private val _isAlternateBouncerVisible = MutableStateFlow(false)
override val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = 0L
private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
override val isAlternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
override val alternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
override fun setPrimaryScrimmed(isScrimmed: Boolean) {
_primaryBouncerScrimmed.value = isScrimmed

View File

@@ -84,9 +84,6 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isUdfpsSupported = MutableStateFlow(false)
private val _isBouncerShowing = MutableStateFlow(false)
override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
private val _isKeyguardGoingAway = MutableStateFlow(false)
override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
@@ -153,10 +150,6 @@ class FakeKeyguardRepository : KeyguardRepository {
_wakefulnessModel.value = model
}
fun setBouncerShowing(isShowing: Boolean) {
_isBouncerShowing.value = isShowing
}
fun setBiometricUnlockState(state: BiometricUnlockModel) {
_biometricUnlockState.tryEmit(state)
}

View File

@@ -49,7 +49,7 @@ public class FakeKeyguardStateController implements KeyguardStateController {
}
@Override
public boolean isBouncerShowing() {
public boolean isPrimaryBouncerShowing() {
return false;
}