Merge changes I645115ab,I73b51987,Id1b79253 into tm-qpr-dev

* changes:
  Scrims - Don't blend with bouncer when shade is collapsing
  Transitions - Add dreaming state
  Transitions - Make them cancelable
This commit is contained in:
Matt Pietal
2022-11-10 20:51:06 +00:00
committed by Android (Google) Code Review
26 changed files with 650 additions and 97 deletions

View File

@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.data.repository package com.android.systemui.keyguard.data.repository
import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position import com.android.systemui.common.shared.model.Position
@@ -69,6 +70,9 @@ interface KeyguardRepository {
*/ */
val isKeyguardShowing: Flow<Boolean> val isKeyguardShowing: Flow<Boolean>
/** Observable for the signal that keyguard is about to go away. */
val isKeyguardGoingAway: Flow<Boolean>
/** Observable for whether the bouncer is showing. */ /** Observable for whether the bouncer is showing. */
val isBouncerShowing: Flow<Boolean> val isBouncerShowing: Flow<Boolean>
@@ -84,6 +88,14 @@ interface KeyguardRepository {
*/ */
val isDozing: Flow<Boolean> val isDozing: Flow<Boolean>
/**
* Observable for whether the device is dreaming.
*
* Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams
* to be active, such as screensavers.
*/
val isDreaming: Flow<Boolean>
/** /**
* Observable for the amount of doze we are currently in. * Observable for the amount of doze we are currently in.
* *
@@ -176,6 +188,29 @@ constructor(
awaitClose { keyguardStateController.removeCallback(callback) } awaitClose { keyguardStateController.removeCallback(callback) }
} }
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
override fun onKeyguardGoingAwayChanged() {
trySendWithFailureLogging(
keyguardStateController.isKeyguardGoingAway,
TAG,
"updated isKeyguardGoingAway"
)
}
}
keyguardStateController.addCallback(callback)
// Adding the callback does not send an initial update.
trySendWithFailureLogging(
keyguardStateController.isKeyguardGoingAway,
TAG,
"initial isKeyguardGoingAway"
)
awaitClose { keyguardStateController.removeCallback(callback) }
}
override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow { override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
val callback = val callback =
object : KeyguardStateController.Callback { object : KeyguardStateController.Callback {
@@ -218,6 +253,25 @@ constructor(
} }
.distinctUntilChanged() .distinctUntilChanged()
override val isDreaming: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
object : KeyguardUpdateMonitorCallback() {
override fun onDreamingStateChanged(isDreaming: Boolean) {
trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
}
}
keyguardUpdateMonitor.registerCallback(callback)
trySendWithFailureLogging(
keyguardUpdateMonitor.isDreaming,
TAG,
"initial isDreaming",
)
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}
.distinctUntilChanged()
override val dozeAmount: Flow<Float> = conflatedCallbackFlow { override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback = val callback =
object : StatusBarStateController.StateListener { object : StatusBarStateController.StateListener {

View File

@@ -94,11 +94,13 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
*/ */
private val _transitions = private val _transitions =
MutableSharedFlow<TransitionStep>( MutableSharedFlow<TransitionStep>(
replay = 2,
extraBufferCapacity = 10, extraBufferCapacity = 10,
onBufferOverflow = BufferOverflow.DROP_OLDEST onBufferOverflow = BufferOverflow.DROP_OLDEST,
) )
override val transitions = _transitions.asSharedFlow().distinctUntilChanged() override val transitions = _transitions.asSharedFlow().distinctUntilChanged()
private var lastStep: TransitionStep = TransitionStep() private var lastStep: TransitionStep = TransitionStep()
private var lastAnimator: ValueAnimator? = null
/* /*
* When manual control of the transition is requested, a unique [UUID] is used as the handle * When manual control of the transition is requested, a unique [UUID] is used as the handle
@@ -106,19 +108,39 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
*/ */
private var updateTransitionId: UUID? = null private var updateTransitionId: UUID? = null
init {
// Seed with transitions signaling a boot into lockscreen state
emitTransition(
TransitionStep(
KeyguardState.OFF,
KeyguardState.LOCKSCREEN,
0f,
TransitionState.STARTED,
)
)
emitTransition(
TransitionStep(
KeyguardState.OFF,
KeyguardState.LOCKSCREEN,
1f,
TransitionState.FINISHED,
)
)
}
override fun startTransition(info: TransitionInfo): UUID? { override fun startTransition(info: TransitionInfo): UUID? {
if (lastStep.transitionState != TransitionState.FINISHED) { if (lastStep.transitionState != TransitionState.FINISHED) {
// Open questions: Log.i(TAG, "Transition still active: $lastStep, canceling")
// * Queue of transitions? buffer of 1?
// * Are transitions cancellable if a new one is triggered?
// * What validation does this need to do?
Log.wtf(TAG, "Transition still active: $lastStep")
return null
} }
val startingValue = 1f - lastStep.value
lastAnimator?.cancel()
lastAnimator = info.animator
info.animator?.let { animator -> info.animator?.let { animator ->
// An animator was provided, so use it to run the transition // An animator was provided, so use it to run the transition
animator.setFloatValues(0f, 1f) animator.setFloatValues(startingValue, 1f)
animator.duration = ((1f - startingValue) * animator.duration).toLong()
val updateListener = val updateListener =
object : AnimatorUpdateListener { object : AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator) { override fun onAnimationUpdate(animation: ValueAnimator) {
@@ -134,15 +156,24 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
val adapter = val adapter =
object : AnimatorListenerAdapter() { object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) { override fun onAnimationStart(animation: Animator) {
emitTransition(TransitionStep(info, 0f, TransitionState.STARTED)) emitTransition(TransitionStep(info, startingValue, TransitionState.STARTED))
} }
override fun onAnimationCancel(animation: Animator) { override fun onAnimationCancel(animation: Animator) {
Log.i(TAG, "Cancelling transition: $info") endAnimation(animation, lastStep.value, TransitionState.CANCELED)
} }
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
emitTransition(TransitionStep(info, 1f, TransitionState.FINISHED)) endAnimation(animation, 1f, TransitionState.FINISHED)
}
private fun endAnimation(
animation: Animator,
value: Float,
state: TransitionState
) {
emitTransition(TransitionStep(info, value, state))
animator.removeListener(this) animator.removeListener(this)
animator.removeUpdateListener(updateListener) animator.removeUpdateListener(updateListener)
lastAnimator = null
} }
} }
animator.addListener(adapter) animator.addListener(adapter)

View File

@@ -20,10 +20,11 @@ import android.animation.ValueAnimator
import com.android.systemui.animation.Interpolators import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.util.kotlin.sample import com.android.systemui.util.kotlin.sample
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@@ -35,18 +36,30 @@ class AodLockscreenTransitionInteractor
@Inject @Inject
constructor( constructor(
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
private val keyguardRepository: KeyguardRepository, private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor("AOD<->LOCKSCREEN") { ) : TransitionInteractor("AOD<->LOCKSCREEN") {
override fun start() { override fun start() {
scope.launch { scope.launch {
keyguardRepository.isDozing /*
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) * Listening to the startedKeyguardTransitionStep (last started step) allows this code
* to interrupt an active transition, as long as they were either going to LOCKSCREEN or
* AOD state. One example is when the user presses the power button in the middle of an
* active transition.
*/
keyguardInteractor.wakefulnessState
.sample(
keyguardTransitionInteractor.startedKeyguardTransitionStep,
{ a, b -> Pair(a, b) }
)
.collect { pair -> .collect { pair ->
val (isDozing, keyguardState) = pair val (wakefulnessState, lastStartedStep) = pair
if (isDozing && keyguardState == KeyguardState.LOCKSCREEN) { if (
isSleepingOrStartingToSleep(wakefulnessState) &&
lastStartedStep.to == KeyguardState.LOCKSCREEN
) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,
@@ -55,7 +68,10 @@ constructor(
getAnimator(), getAnimator(),
) )
) )
} else if (!isDozing && keyguardState == KeyguardState.AOD) { } else if (
isWakingOrStartingToWake(wakefulnessState) &&
lastStartedStep.to == KeyguardState.AOD
) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2022 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.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.launch
@SysUISingleton
class BouncerToGoneTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
) : TransitionInteractor("BOUNCER->GONE") {
private var transitionId: UUID? = null
override fun start() {
listenForKeyguardGoingAway()
}
private fun listenForKeyguardGoingAway() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (isKeyguardGoingAway, keyguardState) = pair
if (isKeyguardGoingAway && keyguardState == KeyguardState.BOUNCER) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.BOUNCER,
to = KeyguardState.GONE,
animator = getAnimator(),
)
)
}
}
}
}
private fun getAnimator(): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
setDuration(TRANSITION_DURATION_MS)
}
}
companion object {
private const val TRANSITION_DURATION_MS = 300L
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright (C) 2022 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.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
class DreamingLockscreenTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor("DREAMING<->LOCKSCREEN") {
override fun start() {
scope.launch {
keyguardInteractor.isDreaming
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (isDreaming, keyguardState) = pair
if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
getAnimator(),
)
)
} else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.DREAMING,
KeyguardState.LOCKSCREEN,
getAnimator(),
)
)
}
}
}
}
private fun getAnimator(): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
setDuration(TRANSITION_DURATION_MS)
}
}
companion object {
private const val TRANSITION_DURATION_MS = 500L
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2022 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.WakefulnessModel.Companion.isSleepingOrStartingToSleep
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
class DreamingToAodTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor("DREAMING->AOD") {
override fun start() {
scope.launch {
keyguardInteractor.wakefulnessState
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
if (
isSleepingOrStartingToSleep(wakefulnessState) &&
keyguardState == KeyguardState.DREAMING
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.DREAMING,
KeyguardState.AOD,
getAnimator(),
)
)
}
}
}
}
private fun getAnimator(): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
setDuration(TRANSITION_DURATION_MS)
}
}
companion object {
private const val TRANSITION_DURATION_MS = 300L
}
}

View File

@@ -41,8 +41,15 @@ constructor(
val dozeAmount: Flow<Float> = repository.dozeAmount val dozeAmount: Flow<Float> = repository.dozeAmount
/** Whether the system is in doze mode. */ /** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing val isDozing: Flow<Boolean> = repository.isDozing
/**
* Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
* but not vice-versa.
*/
val isDreaming: Flow<Boolean> = repository.isDreaming
/** Whether the keyguard is showing or not. */ /** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
/** Whether the keyguard is going away. */
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Whether the bouncer is showing or not. */ /** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */ /** The device wake/sleep state */

View File

@@ -40,12 +40,24 @@ constructor(
keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) } keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
} }
scope.launch {
keyguardInteractor.isBouncerShowing.collect { logger.v("Bouncer showing", it) }
}
scope.launch { keyguardInteractor.isDozing.collect { logger.v("isDozing", it) } }
scope.launch { scope.launch {
interactor.finishedKeyguardTransitionStep.collect { interactor.finishedKeyguardTransitionStep.collect {
logger.i("Finished transition", it) logger.i("Finished transition", it)
} }
} }
scope.launch {
interactor.canceledKeyguardTransitionStep.collect {
logger.i("Canceled transition", it)
}
}
scope.launch { scope.launch {
interactor.startedKeyguardTransitionStep.collect { logger.i("Started transition", it) } interactor.startedKeyguardTransitionStep.collect { logger.i("Started transition", it) }
} }

View File

@@ -42,6 +42,9 @@ constructor(
is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it") is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it")
is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it") is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it") is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is BouncerToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is DreamingLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
is DreamingToAodTransitionInteractor -> Log.d(TAG, "Started $it")
} }
it.start() it.start()
} }

View File

@@ -57,6 +57,14 @@ constructor(
lockscreenToAodTransition, lockscreenToAodTransition,
) )
/* The last [TransitionStep] with a [TransitionState] of STARTED */
val startedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
/* The last [TransitionStep] with a [TransitionState] of CANCELED */
val canceledKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.CANCELED }
/* The last [TransitionStep] with a [TransitionState] of FINISHED */ /* The last [TransitionStep] with a [TransitionState] of FINISHED */
val finishedKeyguardTransitionStep: Flow<TransitionStep> = val finishedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED } repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
@@ -64,8 +72,4 @@ constructor(
/* The last completed [KeyguardState] transition */ /* The last completed [KeyguardState] transition */
val finishedKeyguardState: Flow<KeyguardState> = val finishedKeyguardState: Flow<KeyguardState> =
finishedKeyguardTransitionStep.map { step -> step.to } finishedKeyguardTransitionStep.map { step -> step.to }
/* The last [TransitionStep] with a [TransitionState] of STARTED */
val startedKeyguardTransitionStep: Flow<TransitionStep> =
repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
} }

View File

@@ -56,10 +56,20 @@ constructor(
private fun listenForBouncerHiding() { private fun listenForBouncerHiding() {
scope.launch { scope.launch {
keyguardInteractor.isBouncerShowing keyguardInteractor.isBouncerShowing
.sample(keyguardInteractor.wakefulnessState, { a, b -> Pair(a, b) }) .sample(
.collect { pair -> combine(
val (isBouncerShowing, wakefulnessState) = pair keyguardInteractor.wakefulnessState,
if (!isBouncerShowing) { keyguardTransitionInteractor.startedKeyguardTransitionStep,
) { a, b ->
Pair(a, b)
},
{ a, bc -> Triple(a, bc.first, bc.second) }
)
.collect { triple ->
val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
val to = val to =
if ( if (
wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP || wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
@@ -90,10 +100,10 @@ constructor(
combine( combine(
keyguardTransitionInteractor.finishedKeyguardState, keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState, keyguardInteractor.statusBarState,
) { keyguardState, statusBarState -> ) { a, b ->
Pair(keyguardState, statusBarState) Pair(a, b)
}, },
{ shadeModel, pair -> Triple(shadeModel, pair.first, pair.second) } { a, bc -> Triple(a, bc.first, bc.second) }
) )
.collect { triple -> .collect { triple ->
val (shadeModel, keyguardState, statusBarState) = triple val (shadeModel, keyguardState, statusBarState) = triple
@@ -116,8 +126,7 @@ constructor(
) )
} else { } else {
// TODO (b/251849525): Remove statusbarstate check when that state is // TODO (b/251849525): Remove statusbarstate check when that state is
// integrated // integrated into KeyguardTransitionRepository
// into KeyguardTransitionRepository
if ( if (
keyguardState == KeyguardState.LOCKSCREEN && keyguardState == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging && shadeModel.isUserDragging &&

View File

@@ -23,6 +23,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
@@ -34,13 +35,17 @@ class LockscreenGoneTransitionInteractor
constructor( constructor(
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor, private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository,
) : TransitionInteractor("LOCKSCREEN->GONE") { ) : TransitionInteractor("LOCKSCREEN->GONE") {
override fun start() { override fun start() {
scope.launch { scope.launch {
keyguardInteractor.isKeyguardShowing.collect { isShowing -> keyguardInteractor.isKeyguardGoingAway
if (!isShowing) { .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (isKeyguardGoingAway, keyguardState) = pair
if (!isKeyguardGoingAway && keyguardState == KeyguardState.LOCKSCREEN) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,

View File

@@ -210,9 +210,12 @@ constructor(
expansion == KeyguardBouncer.EXPANSION_HIDDEN && expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
) { ) {
repository.setPrimaryVisible(false) /*
repository.setPrimaryShow(null) * There are cases where #hide() was not invoked, such as when
falsingCollector.onBouncerHidden() * NotificationPanelViewController controls the hide animation. Make sure the state gets
* updated by calling #hide() directly.
*/
hide()
DejankUtils.postAfterTraversal { primaryBouncerCallbackInteractor.dispatchReset() } DejankUtils.postAfterTraversal { primaryBouncerCallbackInteractor.dispatchReset() }
primaryBouncerCallbackInteractor.dispatchFullyHidden() primaryBouncerCallbackInteractor.dispatchFullyHidden()
} else if ( } else if (

View File

@@ -44,7 +44,21 @@ abstract class StartKeyguardTransitionModule {
@Binds @IntoSet abstract fun aodGone(impl: AodToGoneTransitionInteractor): TransitionInteractor @Binds @IntoSet abstract fun aodGone(impl: AodToGoneTransitionInteractor): TransitionInteractor
@Binds
@IntoSet
abstract fun bouncerGone(impl: BouncerToGoneTransitionInteractor): TransitionInteractor
@Binds @Binds
@IntoSet @IntoSet
abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
@Binds
@IntoSet
abstract fun dreamingLockscreen(
impl: DreamingLockscreenTransitionInteractor
): TransitionInteractor
@Binds
@IntoSet
abstract fun dreamingToAod(impl: DreamingToAodTransitionInteractor): TransitionInteractor
} }

View File

@@ -17,12 +17,29 @@ package com.android.systemui.keyguard.shared.model
/** List of all possible states to transition to/from */ /** List of all possible states to transition to/from */
enum class KeyguardState { enum class KeyguardState {
/** /*
* For initialization as well as when the security method is set to NONE, indicating that * The display is completely off, as well as any sensors that would trigger the device to wake
* the keyguard should never be shown. * up.
*/
OFF,
/**
* The device has entered a special low-power mode within SystemUI. Doze is technically a
* special dream service implementation. No UI is visible. In this state, a least some
* low-powered sensors such as lift to wake or tap to wake are enabled, or wake screen for
* notifications is enabled, allowing the device to quickly wake up.
*/
DOZING,
/*
* A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
* DOZING is an example of special version of this state. Dreams may be implemented by third
* parties to present their own UI over keyguard, like a screensaver.
*/
DREAMING,
/**
* The device has entered a special low-power mode within SystemUI, also called the Always-on
* Display (AOD). A minimal UI is presented to show critical information. If the device is in
* low-power mode without a UI, then it is DOZING.
*/ */
NONE,
/* Always-on Display. The device is in a low-power mode with a minimal UI visible */
AOD, AOD,
/* /*
* The security screen prompt UI, containing PIN, Password, Pattern, and all FPS * The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
@@ -34,7 +51,6 @@ enum class KeyguardState {
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false. * unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
*/ */
LOCKSCREEN, LOCKSCREEN,
/* /*
* Keyguard is no longer visible. In most cases the user has just authenticated and keyguard * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
* is being removed, but there are other cases where the user is swiping away keyguard, such as * is being removed, but there are other cases where the user is swiping away keyguard, such as

View File

@@ -17,7 +17,12 @@ package com.android.systemui.keyguard.shared.model
/** Possible states for a running transition between [State] */ /** Possible states for a running transition between [State] */
enum class TransitionState { enum class TransitionState {
/* Transition has begun. */
STARTED, STARTED,
/* Transition is actively running. */
RUNNING, RUNNING,
FINISHED /* Transition has completed successfully. */
FINISHED,
/* Transition has been interrupted, and not completed successfully. */
CANCELED,
} }

View File

@@ -17,8 +17,8 @@ package com.android.systemui.keyguard.shared.model
/** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */ /** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
data class TransitionStep( data class TransitionStep(
val from: KeyguardState = KeyguardState.NONE, val from: KeyguardState = KeyguardState.OFF,
val to: KeyguardState = KeyguardState.NONE, val to: KeyguardState = KeyguardState.OFF,
val value: Float = 0f, // constrained [0.0, 1.0] val value: Float = 0f, // constrained [0.0, 1.0]
val transitionState: TransitionState = TransitionState.FINISHED, val transitionState: TransitionState = TransitionState.FINISHED,
val ownerName: String = "", val ownerName: String = "",

View File

@@ -24,5 +24,15 @@ enum class WakefulnessModel {
/** Device is now fully awake and interactive. */ /** Device is now fully awake and interactive. */
AWAKE, AWAKE,
/** Signal that the device is now going to sleep. */ /** Signal that the device is now going to sleep. */
STARTING_TO_SLEEP, STARTING_TO_SLEEP;
companion object {
fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
return model == ASLEEP || model == STARTING_TO_SLEEP
}
fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
return model == AWAKE || model == STARTING_TO_WAKE
}
}
} }

View File

@@ -279,10 +279,7 @@ public class KeyguardBouncer {
* @see #onFullyShown() * @see #onFullyShown()
*/ */
private void onFullyHidden() { private void onFullyHidden() {
cancelShowRunnable();
setVisibility(View.INVISIBLE);
mFalsingCollector.onBouncerHidden();
DejankUtils.postAfterTraversal(mResetRunnable);
} }
private void setVisibility(@View.Visibility int visibility) { private void setVisibility(@View.Visibility int visibility) {
@@ -459,7 +456,13 @@ public class KeyguardBouncer {
onFullyShown(); onFullyShown();
dispatchFullyShown(); dispatchFullyShown();
} else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) { } else if (fraction == EXPANSION_HIDDEN && oldExpansion != EXPANSION_HIDDEN) {
onFullyHidden(); DejankUtils.postAfterTraversal(mResetRunnable);
/*
* There are cases where #hide() was not invoked, such as when
* NotificationPanelViewController controls the hide animation. Make sure the state gets
* updated by calling #hide() directly.
*/
hide(false /* destroyView */);
dispatchFullyHidden(); dispatchFullyHidden();
} else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) { } else if (fraction != EXPANSION_VISIBLE && oldExpansion == EXPANSION_VISIBLE) {
dispatchStartingToHide(); dispatchStartingToHide();

View File

@@ -895,7 +895,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha(); float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha();
float behindAlpha; float behindAlpha;
int behindTint; int behindTint = state.getBehindTint();
if (mDarkenWhileDragging) { if (mDarkenWhileDragging) {
behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind, behindAlpha = MathUtils.lerp(mDefaultScrimAlpha, stateBehind,
interpolatedFract); interpolatedFract);
@@ -903,6 +903,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
behindAlpha = MathUtils.lerp(0 /* start */, stateBehind, behindAlpha = MathUtils.lerp(0 /* start */, stateBehind,
interpolatedFract); interpolatedFract);
} }
if (mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()) {
if (mClipsQsScrim) { if (mClipsQsScrim) {
behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(), behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getNotifTint(),
state.getNotifTint(), interpolatedFract); state.getNotifTint(), interpolatedFract);
@@ -910,6 +911,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(), behindTint = ColorUtils.blendARGB(ScrimState.BOUNCER.getBehindTint(),
state.getBehindTint(), interpolatedFract); state.getBehindTint(), interpolatedFract);
} }
}
if (mQsExpansion > 0) { if (mQsExpansion > 0) {
behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion); behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
float tintProgress = mQsExpansion; float tintProgress = mQsExpansion;

View File

@@ -165,6 +165,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override @Override
public void onFullyHidden() { public void onFullyHidden() {
mPrimaryBouncerAnimating = false; mPrimaryBouncerAnimating = false;
updateStates();
} }
@Override @Override
@@ -1184,12 +1185,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
updateNavigationBarVisibility(navBarVisible); updateNavigationBarVisibility(navBarVisible);
} }
if (primaryBouncerShowing != mLastPrimaryBouncerShowing || mFirstUpdate) { boolean isPrimaryBouncerShowingChanged =
primaryBouncerShowing != mLastPrimaryBouncerShowing;
mLastPrimaryBouncerShowing = primaryBouncerShowing;
if (isPrimaryBouncerShowingChanged || mFirstUpdate) {
mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing); mNotificationShadeWindowController.setBouncerShowing(primaryBouncerShowing);
mCentralSurfaces.setBouncerShowing(primaryBouncerShowing); mCentralSurfaces.setBouncerShowing(primaryBouncerShowing);
} }
if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate if (primaryBouncerIsOrWillBeShowing != mLastPrimaryBouncerIsOrWillBeShowing || mFirstUpdate
|| primaryBouncerShowing != mLastPrimaryBouncerShowing) { || isPrimaryBouncerShowingChanged) {
mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing, mKeyguardUpdateManager.sendPrimaryBouncerChanged(primaryBouncerIsOrWillBeShowing,
primaryBouncerShowing); primaryBouncerShowing);
} }
@@ -1198,7 +1203,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mLastShowing = showing; mLastShowing = showing;
mLastGlobalActionsVisible = mGlobalActionsVisible; mLastGlobalActionsVisible = mGlobalActionsVisible;
mLastOccluded = occluded; mLastOccluded = occluded;
mLastPrimaryBouncerShowing = primaryBouncerShowing;
mLastPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing; mLastPrimaryBouncerIsOrWillBeShowing = primaryBouncerIsOrWillBeShowing;
mLastBouncerDismissible = primaryBouncerDismissible; mLastBouncerDismissible = primaryBouncerDismissible;
mLastRemoteInputActive = remoteInputActive; mLastRemoteInputActive = remoteInputActive;

View File

@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost import com.android.systemui.doze.DozeHost
@@ -256,6 +257,48 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun isKeyguardGoingAway() = runBlockingTest {
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
captor.value.onKeyguardGoingAwayChanged()
assertThat(latest).isTrue()
whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
captor.value.onKeyguardGoingAwayChanged()
assertThat(latest).isFalse()
job.cancel()
}
@Test
fun isDreaming() = runBlockingTest {
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
verify(keyguardUpdateMonitor).registerCallback(captor.capture())
captor.value.onDreamingStateChanged(true)
assertThat(latest).isTrue()
captor.value.onDreamingStateChanged(false)
assertThat(latest).isFalse()
job.cancel()
}
@Test @Test
fun biometricUnlockState() = runBlockingTest { fun biometricUnlockState() = runBlockingTest {
val values = mutableListOf<BiometricUnlockModel>() val values = mutableListOf<BiometricUnlockModel>()

View File

@@ -25,8 +25,8 @@ import android.view.Choreographer.FrameCallback
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators import com.android.systemui.animation.Interpolators
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.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionInfo import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
@@ -38,7 +38,6 @@ import java.util.UUID
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@@ -91,18 +90,51 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
} }
} }
assertSteps(steps, listWithStep(BigDecimal(.1))) assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN)
job.cancel() job.cancel()
provider.stop() provider.stop()
} }
@Test @Test
fun `startTransition called during another transition fails`() { fun `starting second transition will cancel the first transition`() {
underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, null)) runBlocking(IMMEDIATE) {
underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, BOUNCER, null)) val (animator, provider) = setupAnimator(this)
assertThat(wtfHandler.failed).isTrue() val steps = mutableListOf<TransitionStep>()
val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
underTest.startTransition(TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, animator))
// 3 yields(), alternating with the animator, results in a value 0.1, which can be
// canceled and tested against
yield()
yield()
yield()
// Now start 2nd transition, which will interrupt the first
val job2 = underTest.transition(LOCKSCREEN, AOD).onEach { steps.add(it) }.launchIn(this)
val (animator2, provider2) = setupAnimator(this)
underTest.startTransition(TransitionInfo(OWNER_NAME, LOCKSCREEN, AOD, animator2))
val startTime = System.currentTimeMillis()
while (animator2.isRunning()) {
yield()
if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
}
}
val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.9))
assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
job.cancel()
job2.cancel()
provider.stop()
provider2.stop()
}
} }
@Test @Test
@@ -165,11 +197,15 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(wtfHandler.failed).isTrue() assertThat(wtfHandler.failed).isTrue()
} }
private fun listWithStep(step: BigDecimal): List<BigDecimal> { private fun listWithStep(
step: BigDecimal,
start: BigDecimal = BigDecimal.ZERO,
stop: BigDecimal = BigDecimal.ONE,
): List<BigDecimal> {
val steps = mutableListOf<BigDecimal>() val steps = mutableListOf<BigDecimal>()
var i = BigDecimal.ZERO var i = start
while (i.compareTo(BigDecimal.ONE) <= 0) { while (i.compareTo(stop) <= 0) {
steps.add(i) steps.add(i)
i = (i + step).setScale(2, RoundingMode.HALF_UP) i = (i + step).setScale(2, RoundingMode.HALF_UP)
} }
@@ -177,23 +213,43 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
return steps return steps
} }
private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) { private fun assertSteps(
steps: List<TransitionStep>,
fractions: List<BigDecimal>,
from: KeyguardState,
to: KeyguardState,
) {
assertThat(steps[0]) assertThat(steps[0])
.isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME)) .isEqualTo(
TransitionStep(
from,
to,
fractions[0].toFloat(),
TransitionState.STARTED,
OWNER_NAME
)
)
fractions.forEachIndexed { index, fraction -> fractions.forEachIndexed { index, fraction ->
assertThat(steps[index + 1]) assertThat(steps[index + 1])
.isEqualTo( .isEqualTo(
TransitionStep( TransitionStep(
AOD, from,
LOCKSCREEN, to,
fraction.toFloat(), fraction.toFloat(),
TransitionState.RUNNING, TransitionState.RUNNING,
OWNER_NAME OWNER_NAME
) )
) )
} }
val lastValue = fractions[fractions.size - 1].toFloat()
val status =
if (lastValue < 1f) {
TransitionState.CANCELED
} else {
TransitionState.FINISHED
}
assertThat(steps[steps.size - 1]) assertThat(steps[steps.size - 1])
.isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME)) .isEqualTo(TransitionStep(from, to, lastValue, status, OWNER_NAME))
assertThat(wtfHandler.failed).isFalse() assertThat(wtfHandler.failed).isFalse()
} }
@@ -230,7 +286,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
scope.launch { scope.launch {
frames.collect { frames.collect {
// Delay is required for AnimationHandler to properly register a callback // Delay is required for AnimationHandler to properly register a callback
delay(1) yield()
val (frameNumber, callback) = it val (frameNumber, callback) = it
callback?.doFrame(frameNumber) callback?.doFrame(frameNumber)
} }
@@ -243,7 +299,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
} }
override fun postFrameCallback(cb: FrameCallback) { override fun postFrameCallback(cb: FrameCallback) {
frames.value = Pair(++frameCount, cb) frames.value = Pair(frameCount++, cb)
} }
override fun postCommitCallback(runnable: Runnable) {} override fun postCommitCallback(runnable: Runnable) {}
override fun getFrameTime() = frameCount override fun getFrameTime() = frameCount

View File

@@ -155,6 +155,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN) mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
verify(repository).setPrimaryVisible(false) verify(repository).setPrimaryVisible(false)
verify(repository).setPrimaryShow(null) verify(repository).setPrimaryShow(null)
verify(repository).setPrimaryHide(true)
verify(falsingCollector).onBouncerHidden() verify(falsingCollector).onBouncerHidden()
verify(mPrimaryBouncerCallbackInteractor).dispatchReset() verify(mPrimaryBouncerCallbackInteractor).dispatchReset()
verify(mPrimaryBouncerCallbackInteractor).dispatchFullyHidden() verify(mPrimaryBouncerCallbackInteractor).dispatchFullyHidden()

View File

@@ -1436,6 +1436,17 @@ public class ScrimControllerTest extends SysuiTestCase {
assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion);
} }
@Test
public void behindTint_inKeyguardState_bouncerNotActive_usesKeyguardBehindTint() {
when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
mScrimController.setClipsQsScrim(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
finishAnimationsImmediately();
assertThat(mScrimBehind.getTint())
.isEqualTo(ScrimState.KEYGUARD.getBehindTint());
}
@Test @Test
public void testNotificationTransparency_followsTransitionToFullShade() { public void testNotificationTransparency_followsTransitionToFullShade() {
mScrimController.transitionTo(SHADE_LOCKED); mScrimController.transitionTo(SHADE_LOCKED);

View File

@@ -44,6 +44,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isDozing = MutableStateFlow(false) private val _isDozing = MutableStateFlow(false)
override val isDozing: Flow<Boolean> = _isDozing override val isDozing: Flow<Boolean> = _isDozing
private val _isDreaming = MutableStateFlow(false)
override val isDreaming: Flow<Boolean> = _isDreaming
private val _dozeAmount = MutableStateFlow(0f) private val _dozeAmount = MutableStateFlow(0f)
override val dozeAmount: Flow<Float> = _dozeAmount override val dozeAmount: Flow<Float> = _dozeAmount
@@ -58,6 +61,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isBouncerShowing = MutableStateFlow(false) private val _isBouncerShowing = MutableStateFlow(false)
override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
private val _isKeyguardGoingAway = MutableStateFlow(false)
override val isKeyguardGoingAway: Flow<Boolean> = _isKeyguardGoingAway
private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE) private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState