[DO NOT MERGE] Transitions - Smooth out bouncer -> gone

If the device unlocks while the bouncer is in transit, the bouncer
will remain visible over the launcher for half a second while the
appear animation completes while the scrim immediately
disappears (while also undergoing numerous bad requests to transition
to invalid states).

To fix, control this animation using the transition repo instead and
ignore all other incoming requests while it is running.

Also, support a new mode for canceling transitions. Some transitions
may want to continue from the canceled point, others may want to run
the starting transition in full.

Fixes: 268596047
Test: ScrimControllerTest KeyguardRepositoryImplTest
NotificationShadeWindowViewControllerTest
KeyguardTransitionScenariosTest KeyguardTransitionRepositoryTest
Test: manual - Tested all bouncer variations, with and without face
unlock

Change-Id: I6fdd0b796e8833539d06c8534182f1298c7e3a58
This commit is contained in:
Matt Pietal
2023-02-17 14:05:34 +00:00
parent 99dc404b91
commit 0f04f905ae
23 changed files with 445 additions and 230 deletions

View File

@@ -78,7 +78,7 @@
<!-- The vertical margin between the date and the owner info. --> <!-- The vertical margin between the date and the owner info. -->
<!-- The translation for disappearing security views after having solved them. --> <!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen> <dimen name="disappear_y_translation">-50dp</dimen>
<!-- Dimens for animation for the Bouncer PIN view --> <!-- Dimens for animation for the Bouncer PIN view -->
<dimen name="pin_view_trans_y_entry">120dp</dimen> <dimen name="pin_view_trans_y_entry">120dp</dimen>

View File

@@ -39,7 +39,6 @@ import static java.lang.Integer.max;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.app.Activity; import android.app.Activity;
@@ -1044,13 +1043,10 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation); int yTranslation = mResources.getDimensionPixelSize(R.dimen.disappear_y_translation);
AnimatorSet anims = new AnimatorSet();
ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation); ObjectAnimator yAnim = ObjectAnimator.ofFloat(mView, View.TRANSLATION_Y, yTranslation);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mView, View.ALPHA, 0f); yAnim.setInterpolator(Interpolators.STANDARD_ACCELERATE);
yAnim.setDuration(500);
anims.setInterpolator(Interpolators.STANDARD_ACCELERATE); yAnim.start();
anims.playTogether(alphaAnim, yAnim);
anims.start();
} }
private void setupUserSwitcher() { private void setupUserSwitcher() {

View File

@@ -636,12 +636,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
public void startAppearAnimation() { public void startAppearAnimation() {
if (mCurrentSecurityMode != SecurityMode.None) { if (mCurrentSecurityMode != SecurityMode.None) {
mView.setAlpha(1f); setAlpha(1f);
mView.startAppearAnimation(mCurrentSecurityMode); mView.startAppearAnimation(mCurrentSecurityMode);
getCurrentSecurityController().startAppearAnimation(); getCurrentSecurityController().startAppearAnimation();
} }
} }
/** Set the alpha of the security container view */
public void setAlpha(float alpha) {
mView.setAlpha(alpha);
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) { public boolean startDisappearAnimation(Runnable onFinishRunnable) {
boolean didRunAnimation = false; boolean didRunAnimation = false;

View File

@@ -80,6 +80,9 @@ interface KeyguardRepository {
*/ */
val isKeyguardShowing: Flow<Boolean> val isKeyguardShowing: Flow<Boolean>
/** Is the keyguard in a unlocked state? */
val isKeyguardUnlocked: Flow<Boolean>
/** Is an activity showing over the keyguard? */ /** Is an activity showing over the keyguard? */
val isKeyguardOccluded: Flow<Boolean> val isKeyguardOccluded: Flow<Boolean>
@@ -278,6 +281,31 @@ constructor(
} }
.distinctUntilChanged() .distinctUntilChanged()
override val isKeyguardUnlocked: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
object : KeyguardStateController.Callback {
override fun onUnlockedChanged() {
trySendWithFailureLogging(
keyguardStateController.isUnlocked,
TAG,
"updated isKeyguardUnlocked"
)
}
}
keyguardStateController.addCallback(callback)
// Adding the callback does not send an initial update.
trySendWithFailureLogging(
keyguardStateController.isUnlocked,
TAG,
"initial isKeyguardUnlocked"
)
awaitClose { keyguardStateController.removeCallback(callback) }
}
.distinctUntilChanged()
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
val callback = val callback =
object : KeyguardStateController.Callback { object : KeyguardStateController.Callback {

View File

@@ -68,8 +68,11 @@ interface KeyguardTransitionRepository {
/** /**
* Begin a transition from one state to another. Transitions are interruptible, and will issue a * Begin a transition from one state to another. Transitions are interruptible, and will issue a
* [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one. * [TransitionStep] with state = [TransitionState.CANCELED] before beginning the next one.
*
* When canceled, there are two options: to continue from the current position of the prior
* transition, or to reset the position. When [resetIfCanceled] == true, it will do the latter.
*/ */
fun startTransition(info: TransitionInfo): UUID? fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean = false): UUID?
/** /**
* Allows manual control of a transition. When calling [startTransition], the consumer must pass * Allows manual control of a transition. When calling [startTransition], the consumer must pass
@@ -130,7 +133,10 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
) )
} }
override fun startTransition(info: TransitionInfo): UUID? { override fun startTransition(
info: TransitionInfo,
resetIfCanceled: Boolean,
): UUID? {
if (lastStep.from == info.from && lastStep.to == info.to) { if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info") Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return null return null
@@ -138,7 +144,11 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
val startingValue = val startingValue =
if (lastStep.transitionState != TransitionState.FINISHED) { if (lastStep.transitionState != TransitionState.FINISHED) {
Log.i(TAG, "Transition still active: $lastStep, canceling") Log.i(TAG, "Transition still active: $lastStep, canceling")
if (resetIfCanceled) {
0f
} else {
lastStep.value lastStep.value
}
} else { } else {
0f 0f
} }
@@ -227,10 +237,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
} }
private fun trace(step: TransitionStep, isManual: Boolean) { private fun trace(step: TransitionStep, isManual: Boolean) {
if ( if (step.transitionState == TransitionState.RUNNING) {
step.transitionState != TransitionState.STARTED &&
step.transitionState != TransitionState.FINISHED
) {
return return
} }
val traceName = val traceName =
@@ -243,7 +250,10 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
val traceCookie = traceName.hashCode() val traceCookie = traceName.hashCode()
if (step.transitionState == TransitionState.STARTED) { if (step.transitionState == TransitionState.STARTED) {
Trace.beginAsyncSection(traceName, traceCookie) Trace.beginAsyncSection(traceName, traceCookie)
} else if (step.transitionState == TransitionState.FINISHED) { } else if (
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
) {
Trace.endAsyncSection(traceName, traceCookie) Trace.endAsyncSection(traceName, traceCookie)
} }
} }

View File

@@ -34,7 +34,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -57,14 +56,7 @@ constructor(
private fun listenForDreamingToLockscreen() { private fun listenForDreamingToLockscreen() {
scope.launch { scope.launch {
// Dependending on the dream, either dream state or occluded change will change first, keyguardInteractor.isAbleToDream
// so listen for both
combine(keyguardInteractor.isAbleToDream, keyguardInteractor.isKeyguardOccluded) {
isAbleToDream,
isKeyguardOccluded ->
isAbleToDream && isKeyguardOccluded
}
.distinctUntilChanged()
.sample( .sample(
combine( combine(
keyguardInteractor.dozeTransitionModel, keyguardInteractor.dozeTransitionModel,

View File

@@ -130,15 +130,17 @@ constructor(
shadeRepository.shadeModel shadeRepository.shadeModel
.sample( .sample(
combine( combine(
keyguardTransitionInteractor.finishedKeyguardState, keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.statusBarState, keyguardInteractor.statusBarState,
::Pair keyguardInteractor.isKeyguardUnlocked,
),
::toTriple ::toTriple
),
::toQuad
) )
.collect { (shadeModel, keyguardState, statusBarState) -> .collect { (shadeModel, keyguardState, statusBarState, isKeyguardUnlocked) ->
val id = transitionId val id = transitionId
if (id != null) { if (id != null) {
if (keyguardState.to == KeyguardState.PRIMARY_BOUNCER) {
// An existing `id` means a transition is started, and calls to // An existing `id` means a transition is started, and calls to
// `updateTransition` will control it until FINISHED or CANCELED // `updateTransition` will control it until FINISHED or CANCELED
var nextState = var nextState =
@@ -173,12 +175,14 @@ constructor(
) )
) )
} }
}
} else { } else {
// TODO (b/251849525): Remove statusbarstate check when that state is // TODO (b/251849525): Remove statusbarstate check when that state is
// integrated into KeyguardTransitionRepository // integrated into KeyguardTransitionRepository
if ( if (
keyguardState == KeyguardState.LOCKSCREEN && keyguardState.to == KeyguardState.LOCKSCREEN &&
shadeModel.isUserDragging && shadeModel.isUserDragging &&
!isKeyguardUnlocked &&
statusBarState == KEYGUARD statusBarState == KEYGUARD
) { ) {
transitionId = transitionId =

View File

@@ -17,6 +17,9 @@
package com.android.systemui.keyguard.domain.interactor package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator import android.animation.ValueAnimator
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.Password
import com.android.keyguard.KeyguardUpdateMonitor
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
@@ -26,6 +29,8 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample import com.android.systemui.util.kotlin.sample
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -37,7 +42,8 @@ constructor(
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor, private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardSecurityModel: KeyguardSecurityModel,
) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromPrimaryBouncerTransitionInteractor::class.simpleName!!) {
override fun start() { override fun start() {
@@ -93,31 +99,47 @@ constructor(
private fun listenForPrimaryBouncerToGone() { private fun listenForPrimaryBouncerToGone() {
scope.launch { scope.launch {
keyguardInteractor.isKeyguardGoingAway keyguardInteractor.isKeyguardGoingAway
.sample(keyguardTransitionInteractor.finishedKeyguardState) { a, b -> Pair(a, b) } .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair -> .collect { (isKeyguardGoingAway, lastStartedTransitionStep) ->
val (isKeyguardGoingAway, keyguardState) = pair if (
if (isKeyguardGoingAway && keyguardState == KeyguardState.PRIMARY_BOUNCER) { isKeyguardGoingAway &&
lastStartedTransitionStep.to == KeyguardState.PRIMARY_BOUNCER
) {
val securityMode =
keyguardSecurityModel.getSecurityMode(
KeyguardUpdateMonitor.getCurrentUser()
)
// IME for password requires a slightly faster animation
val duration =
if (securityMode == Password) {
TO_GONE_SHORT_DURATION
} else {
TO_GONE_DURATION
}
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
ownerName = name, ownerName = name,
from = KeyguardState.PRIMARY_BOUNCER, from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.GONE, to = KeyguardState.GONE,
animator = getAnimator(), animator = getAnimator(duration),
) ),
resetIfCanceled = true,
) )
} }
} }
} }
} }
private fun getAnimator(): ValueAnimator { private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
return ValueAnimator().apply { return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR) setInterpolator(Interpolators.LINEAR)
setDuration(TRANSITION_DURATION_MS) setDuration(duration.inWholeMilliseconds)
} }
} }
companion object { companion object {
private const val TRANSITION_DURATION_MS = 300L private val DEFAULT_DURATION = 300.milliseconds
val TO_GONE_DURATION = 250.milliseconds
val TO_GONE_SHORT_DURATION = 200.milliseconds
} }
} }

View File

@@ -33,7 +33,9 @@ import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDoz
import com.android.systemui.keyguard.shared.model.DozeTransitionModel import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -95,6 +97,9 @@ constructor(
awaitClose { commandQueue.removeCallback(callback) } awaitClose { commandQueue.removeCallback(callback) }
} }
/** The device wake/sleep state */
val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** /**
* Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means
* that doze mode is not running and DREAMING is ok to commence. * that doze mode is not running and DREAMING is ok to commence.
@@ -109,6 +114,12 @@ constructor(
isDreaming && isDozeOff(dozeTransitionModel.to) isDreaming && isDozeOff(dozeTransitionModel.to)
} }
) )
.sample(
wakefulnessModel,
{ isAbleToDream, wakefulnessModel ->
isAbleToDream && isWakingOrStartingToWake(wakefulnessModel)
}
)
.flatMapLatest { isAbleToDream -> .flatMapLatest { isAbleToDream ->
flow { flow {
delay(50) delay(50)
@@ -119,6 +130,8 @@ constructor(
/** 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 unlocked or not. */
val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked
/** Whether the keyguard is occluded (covered by an activity). */ /** Whether the keyguard is occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */ /** Whether the keyguard is going away. */
@@ -127,8 +140,6 @@ constructor(
val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerVisible
/** Whether the alternate bouncer is showing or not. */ /** Whether the alternate bouncer is showing or not. */
val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
/** The device wake/sleep state */
val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */ /** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState val statusBarState: Flow<StatusBarState> = repository.statusBarState
/** /**

View File

@@ -61,7 +61,15 @@ constructor(
} }
scope.launch { scope.launch {
keyguardInteractor.isDreaming.collect { logger.log(TAG, VERBOSE, "isDreaming", it) } keyguardInteractor.isAbleToDream.collect {
logger.log(TAG, VERBOSE, "isAbleToDream", it)
}
}
scope.launch {
keyguardInteractor.isKeyguardOccluded.collect {
logger.log(TAG, VERBOSE, "isOccluded", it)
}
} }
scope.launch { scope.launch {

View File

@@ -78,6 +78,10 @@ constructor(
val occludedToLockscreenTransition: Flow<TransitionStep> = val occludedToLockscreenTransition: Flow<TransitionStep> =
repository.transition(OCCLUDED, LOCKSCREEN) repository.transition(OCCLUDED, LOCKSCREEN)
/** PRIMARY_BOUNCER->GONE transition information. */
val primaryBouncerToGoneTransition: Flow<TransitionStep> =
repository.transition(PRIMARY_BOUNCER, GONE)
/** /**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
* Lockscreen (0f). * Lockscreen (0f).

View File

@@ -31,6 +31,7 @@ import com.android.settingslib.Utils
import com.android.systemui.keyguard.data.BouncerViewDelegate import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter import com.android.systemui.plugins.ActivityStarter
import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.awaitCancellation
@@ -44,6 +45,7 @@ object KeyguardBouncerViewBinder {
fun bind( fun bind(
view: ViewGroup, view: ViewGroup,
viewModel: KeyguardBouncerViewModel, viewModel: KeyguardBouncerViewModel,
primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
componentFactory: KeyguardBouncerComponent.Factory componentFactory: KeyguardBouncerComponent.Factory
) { ) {
// Builds the KeyguardSecurityContainerController from bouncer view group. // Builds the KeyguardSecurityContainerController from bouncer view group.
@@ -144,6 +146,12 @@ object KeyguardBouncerViewBinder {
} }
} }
launch {
primaryBouncerToGoneTransitionViewModel.bouncerAlpha.collect { alpha ->
securityContainerController.setAlpha(alpha)
}
}
launch { launch {
viewModel.bouncerExpansionAmount viewModel.bouncerExpansionAmount
.filter { it == EXPANSION_VISIBLE } .filter { it == EXPANSION_VISIBLE }

View File

@@ -0,0 +1,58 @@
/*
* 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.ui.viewmodel
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
/**
* Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
* consume.
*/
@SysUISingleton
class PrimaryBouncerToGoneTransitionViewModel
@Inject
constructor(
private val interactor: KeyguardTransitionInteractor,
) {
private val transitionAnimation =
KeyguardTransitionAnimationFlow(
transitionDuration = TO_GONE_DURATION,
transitionFlow = interactor.primaryBouncerToGoneTransition,
)
/** Bouncer container alpha */
val bouncerAlpha: Flow<Float> =
transitionAnimation.createFlow(
duration = 200.milliseconds,
onStep = { 1f - it },
)
/** Scrim alpha */
val scrimAlpha: Flow<Float> =
transitionAnimation.createFlow(
duration = TO_GONE_DURATION,
interpolator = EMPHASIZED_ACCELERATE,
onStep = { 1f - it },
)
}

View File

@@ -45,6 +45,7 @@ import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController; import com.android.systemui.statusbar.NotificationInsetsController;
@@ -133,7 +134,8 @@ public class NotificationShadeWindowViewController {
KeyguardBouncerViewModel keyguardBouncerViewModel, KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor, AlternateBouncerInteractor alternateBouncerInteractor,
KeyguardTransitionInteractor keyguardTransitionInteractor KeyguardTransitionInteractor keyguardTransitionInteractor,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel
) { ) {
mLockscreenShadeTransitionController = transitionController; mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector; mFalsingCollector = falsingCollector;
@@ -160,6 +162,7 @@ public class NotificationShadeWindowViewController {
KeyguardBouncerViewBinder.bind( KeyguardBouncerViewBinder.bind(
mView.findViewById(R.id.keyguard_bouncer_container), mView.findViewById(R.id.keyguard_bouncer_container),
keyguardBouncerViewModel, keyguardBouncerViewModel,
primaryBouncerToGoneTransitionViewModel,
keyguardBouncerComponentFactory); keyguardBouncerComponentFactory);
collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(),

View File

@@ -3695,6 +3695,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override @Override
public void notifyBiometricAuthModeChanged() { public void notifyBiometricAuthModeChanged() {
mDozeServiceHost.updateDozing(); mDozeServiceHost.updateDozing();
if (mBiometricUnlockController.getMode()
== BiometricUnlockController.MODE_DISMISS_BOUNCER) {
// Don't update the scrim controller at this time, in favor of the transition repository
// updating the scrim
return;
}
updateScrimController(); updateScrimController();
} }

View File

@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone; package com.android.systemui.statusbar.phone;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
import static java.lang.Float.isNaN; import static java.lang.Float.isNaN;
import android.animation.Animator; import android.animation.Animator;
@@ -53,7 +55,11 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView; import com.android.systemui.scrim.ScrimView;
import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.notification.stack.ViewState;
@@ -71,6 +77,8 @@ import java.util.function.Consumer;
import javax.inject.Inject; import javax.inject.Inject;
import kotlinx.coroutines.CoroutineDispatcher;
/** /**
* Controls both the scrim behind the notifications and in front of the notifications (when a * Controls both the scrim behind the notifications and in front of the notifications (when a
* security method gets shown). * security method gets shown).
@@ -251,6 +259,28 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private boolean mWakeLockHeld; private boolean mWakeLockHeld;
private boolean mKeyguardOccluded; private boolean mKeyguardOccluded;
private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
private CoroutineDispatcher mMainDispatcher;
private boolean mIsBouncerToGoneTransitionStarted = false;
private boolean mIsBouncerToGoneTransitionRunning = false;
private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
private final Consumer<Float> mScrimAlphaConsumer =
(Float alpha) -> {
mScrimInFront.setViewAlpha(0f);
mNotificationsScrim.setViewAlpha(0f);
mScrimBehind.setViewAlpha(alpha);
};
final Consumer<TransitionStep> mPrimaryBouncerToGoneTransition =
(TransitionStep step) -> {
mIsBouncerToGoneTransitionRunning =
step.getTransitionState() == TransitionState.RUNNING;
mIsBouncerToGoneTransitionStarted =
step.getTransitionState() == TransitionState.STARTED;
if (mIsBouncerToGoneTransitionStarted) {
transitionTo(ScrimState.UNLOCKED);
}
};
@Inject @Inject
public ScrimController( public ScrimController(
LightBarController lightBarController, LightBarController lightBarController,
@@ -265,7 +295,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
@Main Executor mainExecutor, @Main Executor mainExecutor,
ScreenOffAnimationController screenOffAnimationController, ScreenOffAnimationController screenOffAnimationController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) { StatusBarKeyguardViewManager statusBarKeyguardViewManager,
PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
KeyguardTransitionInteractor keyguardTransitionInteractor,
@Main CoroutineDispatcher mainDispatcher) {
mScrimStateListener = lightBarController::setScrimState; mScrimStateListener = lightBarController::setScrimState;
mDefaultScrimAlpha = BUSY_SCRIM_ALPHA; mDefaultScrimAlpha = BUSY_SCRIM_ALPHA;
@@ -304,6 +337,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
} }
}); });
mColors = new GradientColors(); mColors = new GradientColors();
mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
mKeyguardTransitionInteractor = keyguardTransitionInteractor;
mMainDispatcher = mainDispatcher;
} }
/** /**
@@ -343,6 +379,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
for (ScrimState state : ScrimState.values()) { for (ScrimState state : ScrimState.values()) {
state.prepare(state); state.prepare(state);
} }
collectFlow(behindScrim, mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition(),
mPrimaryBouncerToGoneTransition, mMainDispatcher);
collectFlow(behindScrim, mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha(),
mScrimAlphaConsumer, mMainDispatcher);
} }
/** /**
@@ -365,6 +406,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
} }
public void transitionTo(ScrimState state, Callback callback) { public void transitionTo(ScrimState state, Callback callback) {
if (mIsBouncerToGoneTransitionRunning) {
Log.i(TAG, "Skipping transition to: " + state
+ " while mIsBouncerToGoneTransitionRunning");
return;
}
if (state == mState) { if (state == mState) {
// Call the callback anyway, unless it's already enqueued // Call the callback anyway, unless it's already enqueued
if (callback != null && mCallback != callback) { if (callback != null && mCallback != callback) {
@@ -784,10 +830,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
mBehindAlpha = 0; mBehindAlpha = 0;
mNotificationsAlpha = 0; mNotificationsAlpha = 0;
} else { } else {
// Behind scrim will finish fading in at 30% expansion.
float behindFraction = MathUtils float behindFraction = MathUtils
.constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction); .constrainedMap(0f, 1f, 0f, 0.3f, mPanelExpansionFraction);
if (!mIsBouncerToGoneTransitionStarted) {
mBehindAlpha = behindFraction * mDefaultScrimAlpha; mBehindAlpha = behindFraction * mDefaultScrimAlpha;
}
// Delay fade-in of notification scrim a bit further, to coincide with the // Delay fade-in of notification scrim a bit further, to coincide with the
// behind scrim finishing fading in. // behind scrim finishing fading in.
// Also to coincide with the view starting to fade in, otherwise the empty // Also to coincide with the view starting to fade in, otherwise the empty
@@ -1125,7 +1172,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint", Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
Color.alpha(tint)); Color.alpha(tint));
scrimView.setTint(tint); scrimView.setTint(tint);
if (!mIsBouncerToGoneTransitionRunning) {
scrimView.setViewAlpha(alpha); scrimView.setViewAlpha(alpha);
}
} else { } else {
scrim.setAlpha(alpha); scrim.setAlpha(alpha);
} }
@@ -1473,6 +1522,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
} }
public void setKeyguardOccluded(boolean keyguardOccluded) { public void setKeyguardOccluded(boolean keyguardOccluded) {
if (mKeyguardOccluded == keyguardOccluded) {
return;
}
mKeyguardOccluded = keyguardOccluded; mKeyguardOccluded = keyguardOccluded;
updateScrims(); updateScrims();
} }

View File

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

View File

@@ -19,6 +19,8 @@ package com.android.systemui.keyguard.domain.interactor
import android.animation.ValueAnimator import android.animation.ValueAnimator
import androidx.test.filters.FlakyTest import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
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.flags.FakeFeatureFlags import com.android.systemui.flags.FakeFeatureFlags
@@ -40,6 +42,7 @@ import com.android.systemui.keyguard.util.KeyguardTransitionRunner
import com.android.systemui.shade.data.repository.FakeShadeRepository import com.android.systemui.shade.data.repository.FakeShadeRepository
import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.cancelChildren import kotlinx.coroutines.cancelChildren
@@ -51,6 +54,8 @@ import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.JUnit4 import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.reset import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
@@ -77,6 +82,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
// Used to verify transition requests for test output // Used to verify transition requests for test output
@Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository @Mock private lateinit var mockTransitionRepository: KeyguardTransitionRepository
@Mock private lateinit var commandQueue: CommandQueue @Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@@ -102,6 +108,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
transitionRepository = KeyguardTransitionRepositoryImpl() transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository) runner = KeyguardTransitionRunner(transitionRepository)
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) } val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor( FromLockscreenTransitionInteractor(
@@ -173,16 +181,17 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardInteractor = createKeyguardInteractor(featureFlags), keyguardInteractor = createKeyguardInteractor(featureFlags),
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
keyguardSecurityModel = keyguardSecurityModel,
) )
fromPrimaryBouncerTransitionInteractor.start() fromPrimaryBouncerTransitionInteractor.start()
} }
@Test @Test
fun `DREAMING to LOCKSCREEN - dreaming state changes first`() = fun `DREAMING to LOCKSCREEN`() =
testScope.runTest { testScope.runTest {
// GIVEN a device is dreaming and occluded // GIVEN a device is dreaming
keyguardRepository.setDreamingWithOverlay(true) keyguardRepository.setDreamingWithOverlay(true)
keyguardRepository.setKeyguardOccluded(true) keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent() runCurrent()
// GIVEN a prior transition has run to DREAMING // GIVEN a prior transition has run to DREAMING
@@ -215,56 +224,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
}
// THEN a transition to BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DREAMING)
assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `DREAMING to LOCKSCREEN - occluded state changes first`() =
testScope.runTest {
// GIVEN a device is dreaming and occluded
keyguardRepository.setDreamingWithOverlay(true)
keyguardRepository.setKeyguardOccluded(true)
runCurrent()
// GIVEN a prior transition has run to DREAMING
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.DREAMING,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN doze is complete
keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
)
// AND occluded has stopped
keyguardRepository.setKeyguardOccluded(false)
advanceUntilIdle()
// AND then dreaming has stopped
keyguardRepository.setDreamingWithOverlay(false)
advanceUntilIdle()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
} }
// THEN a transition to BOUNCER should occur // THEN a transition to BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromDreamingTransitionInteractor")
@@ -304,7 +264,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to PRIMARY_BOUNCER should occur // THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -345,7 +305,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -386,7 +346,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
@@ -427,7 +387,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -468,7 +428,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
@@ -505,7 +465,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -542,7 +502,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
@@ -583,7 +543,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -624,7 +584,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to AOD should occur // THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -661,7 +621,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to AOD should occur // THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -677,6 +637,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
testScope.runTest { testScope.runTest {
// GIVEN a device that is not dreaming or dozing // GIVEN a device that is not dreaming or dozing
keyguardRepository.setDreamingWithOverlay(false) keyguardRepository.setDreamingWithOverlay(false)
keyguardRepository.setWakefulnessModel(startingToWake())
keyguardRepository.setDozeTransitionModel( keyguardRepository.setDozeTransitionModel(
DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH) DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
) )
@@ -704,7 +665,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DREAMING should occur // THEN a transition to DREAMING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
@@ -741,7 +702,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to PRIMARY_BOUNCER should occur // THEN a transition to PRIMARY_BOUNCER should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -784,7 +745,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to AOD should occur // THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -828,7 +789,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -870,7 +831,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to LOCKSCREEN should occur // THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromAlternateBouncerTransitionInteractor")
@@ -912,7 +873,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to AOD should occur // THEN a transition to AOD should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -954,7 +915,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to DOZING should occur // THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")
@@ -995,7 +956,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
val info = val info =
withArgCaptor<TransitionInfo> { withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture(), anyBoolean())
} }
// THEN a transition to LOCKSCREEN should occur // THEN a transition to LOCKSCREEN should occur
assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromPrimaryBouncerTransitionInteractor")

View File

@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteracto
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationInsetsController import com.android.systemui.statusbar.NotificationInsetsController
@@ -65,48 +66,32 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true) @RunWithLooper(setAsMainLooper = true)
class NotificationShadeWindowViewControllerTest : SysuiTestCase() { class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock @Mock private lateinit var view: NotificationShadeWindowView
private lateinit var view: NotificationShadeWindowView @Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@Mock @Mock private lateinit var centralSurfaces: CentralSurfaces
private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController @Mock private lateinit var dockManager: DockManager
@Mock @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
private lateinit var centralSurfaces: CentralSurfaces @Mock private lateinit var notificationShadeDepthController: NotificationShadeDepthController
@Mock @Mock private lateinit var notificationShadeWindowController: NotificationShadeWindowController
private lateinit var dockManager: DockManager @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock @Mock private lateinit var ambientState: AmbientState
private lateinit var notificationPanelViewController: NotificationPanelViewController @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
@Mock @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
private lateinit var notificationShadeDepthController: NotificationShadeDepthController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock @Mock private lateinit var statusBarWindowStateController: StatusBarWindowStateController
private lateinit var notificationShadeWindowController: NotificationShadeWindowController
@Mock
private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock
private lateinit var ambientState: AmbientState
@Mock
private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
@Mock
private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@Mock
private lateinit var statusBarWindowStateController: StatusBarWindowStateController
@Mock @Mock
private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController
@Mock @Mock private lateinit var lockIconViewController: LockIconViewController
private lateinit var lockIconViewController: LockIconViewController @Mock private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock @Mock private lateinit var pulsingGestureListener: PulsingGestureListener
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController @Mock private lateinit var notificationInsetsController: NotificationInsetsController
@Mock @Mock private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
private lateinit var pulsingGestureListener: PulsingGestureListener
@Mock
private lateinit var notificationInsetsController: NotificationInsetsController
@Mock
private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
@Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
@Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
@Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController
@Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor
@Mock
lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler private lateinit var interactionEventHandler: InteractionEventHandler
@@ -125,7 +110,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
.thenReturn(keyguardSecurityContainerController) .thenReturn(keyguardSecurityContainerController)
whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition) whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
.thenReturn(emptyFlow<TransitionStep>()) .thenReturn(emptyFlow<TransitionStep>())
underTest = NotificationShadeWindowViewController( underTest =
NotificationShadeWindowViewController(
lockscreenShadeTransitionController, lockscreenShadeTransitionController,
FalsingCollectorFake(), FalsingCollectorFake(),
sysuiStatusBarStateController, sysuiStatusBarStateController,
@@ -148,11 +134,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
keyguardBouncerComponentFactory, keyguardBouncerComponentFactory,
alternateBouncerInteractor, alternateBouncerInteractor,
keyguardTransitionInteractor, keyguardTransitionInteractor,
primaryBouncerToGoneTransitionViewModel,
) )
underTest.setupExpandedStatusBar() underTest.setupExpandedStatusBar()
interactionEventHandlerCaptor = interactionEventHandlerCaptor = ArgumentCaptor.forClass(InteractionEventHandler::class.java)
ArgumentCaptor.forClass(InteractionEventHandler::class.java)
verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture())
interactionEventHandler = interactionEventHandlerCaptor.value interactionEventHandler = interactionEventHandlerCaptor.value
} }
@@ -184,14 +170,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Test @Test
fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() { fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() {
underTest.setStatusBarViewController(phoneStatusBarViewController) underTest.setStatusBarViewController(phoneStatusBarViewController)
val downEvBelow = MotionEvent.obtain( val downEvBelow =
0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0 MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0)
)
interactionEventHandler.handleDispatchTouchEvent(downEvBelow) interactionEventHandler.handleDispatchTouchEvent(downEvBelow)
val nextEvent = MotionEvent.obtain( val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0)
0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0
)
whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true) whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true)
val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent) val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent)

View File

@@ -46,6 +46,7 @@ import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationInsetsController; import com.android.systemui.statusbar.NotificationInsetsController;
@@ -101,6 +102,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private NotificationInsetsController mNotificationInsetsController; @Mock private NotificationInsetsController mNotificationInsetsController;
@Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler> @Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor; mInteractionEventHandlerCaptor;
@@ -150,7 +152,8 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mKeyguardBouncerViewModel, mKeyguardBouncerViewModel,
mKeyguardBouncerComponentFactory, mKeyguardBouncerComponentFactory,
mAlternateBouncerInteractor, mAlternateBouncerInteractor,
mKeyguardTransitionInteractor mKeyguardTransitionInteractor,
mPrimaryBouncerToGoneTransitionViewModel
); );
mController.setupExpandedStatusBar(); mController.setupExpandedStatusBar();
mController.setDragDownHelper(mDragDownHelper); mController.setDragDownHelper(mDragDownHelper);

View File

@@ -24,6 +24,8 @@ import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyFloat;
@@ -58,7 +60,12 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ShadeInterpolation; import com.android.systemui.animation.ShadeInterpolation;
import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.keyguard.shared.model.KeyguardState;
import com.android.systemui.keyguard.shared.model.TransitionState;
import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
import com.android.systemui.scrim.ScrimView; import com.android.systemui.scrim.ScrimView;
import com.android.systemui.statusbar.policy.FakeConfigurationController; import com.android.systemui.statusbar.policy.FakeConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -85,8 +92,10 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import kotlinx.coroutines.CoroutineDispatcher;
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper @TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest @SmallTest
public class ScrimControllerTest extends SysuiTestCase { public class ScrimControllerTest extends SysuiTestCase {
@@ -115,6 +124,10 @@ public class ScrimControllerTest extends SysuiTestCase {
@Mock private DockManager mDockManager; @Mock private DockManager mDockManager;
@Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
@Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor;
@Mock private CoroutineDispatcher mMainDispatcher;
// TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The
// event-dispatch-on-registration pattern caused some of these unit tests to fail.) // event-dispatch-on-registration pattern caused some of these unit tests to fail.)
@Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -225,13 +238,20 @@ public class ScrimControllerTest extends SysuiTestCase {
when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock); when(mDelayedWakeLockBuilder.build()).thenReturn(mWakeLock);
when(mDockManager.isDocked()).thenReturn(false); when(mDockManager.isDocked()).thenReturn(false);
when(mKeyguardTransitionInteractor.getPrimaryBouncerToGoneTransition())
.thenReturn(emptyFlow());
when(mPrimaryBouncerToGoneTransitionViewModel.getScrimAlpha()).thenReturn(emptyFlow());
mScrimController = new ScrimController(mLightBarController, mScrimController = new ScrimController(mLightBarController,
mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder, mDozeParameters, mAlarmManager, mKeyguardStateController, mDelayedWakeLockBuilder,
new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor, new FakeHandler(mLooper.getLooper()), mKeyguardUpdateMonitor,
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController, mScreenOffAnimationController,
mKeyguardUnlockAnimationController, mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager); mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mMainDispatcher);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener); mScrimController.setAnimatorListener(mAnimatorListener);
@@ -861,7 +881,10 @@ public class ScrimControllerTest extends SysuiTestCase {
mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()),
mScreenOffAnimationController, mScreenOffAnimationController,
mKeyguardUnlockAnimationController, mKeyguardUnlockAnimationController,
mStatusBarKeyguardViewManager); mStatusBarKeyguardViewManager,
mPrimaryBouncerToGoneTransitionViewModel,
mKeyguardTransitionInteractor,
mMainDispatcher);
mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible);
mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront);
mScrimController.setAnimatorListener(mAnimatorListener); mScrimController.setAnimatorListener(mAnimatorListener);
@@ -1629,6 +1652,18 @@ public class ScrimControllerTest extends SysuiTestCase {
assertScrimAlpha(mScrimBehind, 0); assertScrimAlpha(mScrimBehind, 0);
} }
@Test
public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.mPrimaryBouncerToGoneTransition.accept(
new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
TransitionState.RUNNING, "ScrimControllerTest"));
// This request should not happen
mScrimController.transitionTo(ScrimState.BOUNCER);
assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
}
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion); mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately(); finishAnimationsImmediately();

View File

@@ -47,6 +47,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isKeyguardShowing = MutableStateFlow(false) private val _isKeyguardShowing = MutableStateFlow(false)
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
private val _isKeyguardUnlocked = MutableStateFlow(false)
override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked
private val _isKeyguardOccluded = MutableStateFlow(false) private val _isKeyguardOccluded = MutableStateFlow(false)
override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded

View File

@@ -37,7 +37,7 @@ class FakeKeyguardTransitionRepository : KeyguardTransitionRepository {
_transitions.emit(step) _transitions.emit(step)
} }
override fun startTransition(info: TransitionInfo): UUID? { override fun startTransition(info: TransitionInfo, resetIfCanceled: Boolean): UUID? {
return null return null
} }