Merge "Transitions - Add occlusion state" into tm-qpr-dev

This commit is contained in:
Matt Pietal
2022-12-22 12:39:36 +00:00
committed by Android (Google) Code Review
25 changed files with 640 additions and 252 deletions

View File

@@ -101,13 +101,11 @@ constructor(
transitionViewModel.dreamOverlayTranslationY(it.translationYPx) transitionViewModel.dreamOverlayTranslationY(it.translationYPx)
} }
.collect { px -> .collect { px ->
setElementsTranslationYAtPosition( ComplicationLayoutParams.iteratePositions(
px, { position: Int ->
ComplicationLayoutParams.POSITION_TOP setElementsTranslationYAtPosition(px, position)
) },
setElementsTranslationYAtPosition( POSITION_TOP or POSITION_BOTTOM
px,
ComplicationLayoutParams.POSITION_BOTTOM
) )
} }
} }
@@ -115,15 +113,15 @@ constructor(
/* Alpha animations, when moving from DREAMING->LOCKSCREEN state */ /* Alpha animations, when moving from DREAMING->LOCKSCREEN state */
launch { launch {
transitionViewModel.dreamOverlayAlpha.collect { alpha -> transitionViewModel.dreamOverlayAlpha.collect { alpha ->
setElementsAlphaAtPosition( ComplicationLayoutParams.iteratePositions(
alpha = alpha, { position: Int ->
position = ComplicationLayoutParams.POSITION_TOP, setElementsAlphaAtPosition(
fadingOut = true, alpha = alpha,
) position = position,
setElementsAlphaAtPosition( fadingOut = true,
alpha = alpha, )
position = ComplicationLayoutParams.POSITION_BOTTOM, },
fadingOut = true, POSITION_TOP or POSITION_BOTTOM
) )
} }
} }

View File

@@ -20,26 +20,39 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.CallbackController
import javax.inject.Inject import javax.inject.Inject
/** Dream-related callback information */ /** Dream overlay-related callback information */
@SysUISingleton @SysUISingleton
class DreamCallbackController @Inject constructor() : class DreamOverlayCallbackController @Inject constructor() :
CallbackController<DreamCallbackController.DreamCallback> { CallbackController<DreamOverlayCallbackController.Callback> {
private val callbacks = mutableSetOf<DreamCallbackController.DreamCallback>() private val callbacks = mutableSetOf<DreamOverlayCallbackController.Callback>()
override fun addCallback(callback: DreamCallbackController.DreamCallback) { var isDreaming = false
private set
override fun addCallback(callback: DreamOverlayCallbackController.Callback) {
callbacks.add(callback) callbacks.add(callback)
} }
override fun removeCallback(callback: DreamCallbackController.DreamCallback) { override fun removeCallback(callback: DreamOverlayCallbackController.Callback) {
callbacks.remove(callback) callbacks.remove(callback)
} }
fun onWakeUp() { fun onWakeUp() {
isDreaming = false
callbacks.forEach { it.onWakeUp() } callbacks.forEach { it.onWakeUp() }
} }
interface DreamCallback { fun onStartDream() {
isDreaming = true
callbacks.forEach { it.onStartDream() }
}
interface Callback {
/** Dream overlay has ended */
fun onWakeUp() fun onWakeUp()
/** Dream overlay has started */
fun onStartDream()
} }
} }

View File

@@ -64,7 +64,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// A controller for the dream overlay container view (which contains both the status bar and the // A controller for the dream overlay container view (which contains both the status bar and the
// content area). // content area).
private DreamOverlayContainerViewController mDreamOverlayContainerViewController; private DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final DreamCallbackController mDreamCallbackController; private final DreamOverlayCallbackController mDreamOverlayCallbackController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Nullable @Nullable
private final ComponentName mLowLightDreamComponent; private final ComponentName mLowLightDreamComponent;
@@ -134,7 +134,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
UiEventLogger uiEventLogger, UiEventLogger uiEventLogger,
@Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT) @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
ComponentName lowLightDreamComponent, ComponentName lowLightDreamComponent,
DreamCallbackController dreamCallbackController) { DreamOverlayCallbackController dreamOverlayCallbackController) {
mContext = context; mContext = context;
mExecutor = executor; mExecutor = executor;
mWindowManager = windowManager; mWindowManager = windowManager;
@@ -143,7 +143,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
mStateController = stateController; mStateController = stateController;
mUiEventLogger = uiEventLogger; mUiEventLogger = uiEventLogger;
mDreamCallbackController = dreamCallbackController; mDreamOverlayCallbackController = dreamOverlayCallbackController;
final ViewModelStore viewModelStore = new ViewModelStore(); final ViewModelStore viewModelStore = new ViewModelStore();
final Complication.Host host = final Complication.Host host =
@@ -203,6 +203,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent));
mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START);
mDreamOverlayCallbackController.onStartDream();
mStarted = true; mStarted = true;
}); });
} }
@@ -219,7 +220,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
public void onWakeUp(@NonNull Runnable onCompletedCallback) { public void onWakeUp(@NonNull Runnable onCompletedCallback) {
mExecutor.execute(() -> { mExecutor.execute(() -> {
if (mDreamOverlayContainerViewController != null) { if (mDreamOverlayContainerViewController != null) {
mDreamCallbackController.onWakeUp(); mDreamOverlayCallbackController.onWakeUp();
mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor); mDreamOverlayContainerViewController.wakeUp(onCompletedCallback, mExecutor);
} }
}); });

View File

@@ -29,8 +29,7 @@ import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamCallbackController import com.android.systemui.dreams.DreamOverlayCallbackController
import com.android.systemui.dreams.DreamCallbackController.DreamCallback
import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -49,7 +48,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.merge
/** Defines interface for classes that encapsulate application state for the keyguard. */ /** Defines interface for classes that encapsulate application state for the keyguard. */
interface KeyguardRepository { interface KeyguardRepository {
@@ -81,6 +79,9 @@ interface KeyguardRepository {
*/ */
val isKeyguardShowing: Flow<Boolean> val isKeyguardShowing: Flow<Boolean>
/** Is an activity showing over the keyguard? */
val isKeyguardOccluded: Flow<Boolean>
/** Observable for the signal that keyguard is about to go away. */ /** Observable for the signal that keyguard is about to go away. */
val isKeyguardGoingAway: Flow<Boolean> val isKeyguardGoingAway: Flow<Boolean>
@@ -107,6 +108,9 @@ interface KeyguardRepository {
*/ */
val isDreaming: Flow<Boolean> val isDreaming: Flow<Boolean>
/** Observable for whether the device is dreaming with an overlay, see [DreamOverlayService] */
val isDreamingWithOverlay: Flow<Boolean>
/** /**
* Observable for the amount of doze we are currently in. * Observable for the amount of doze we are currently in.
* *
@@ -179,7 +183,7 @@ constructor(
private val keyguardUpdateMonitor: KeyguardUpdateMonitor, private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener, private val dozeTransitionListener: DozeTransitionListener,
private val authController: AuthController, private val authController: AuthController,
private val dreamCallbackController: DreamCallbackController, private val dreamOverlayCallbackController: DreamOverlayCallbackController,
) : KeyguardRepository { ) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions = override val animateBottomAreaDozingTransitions =
@@ -191,28 +195,55 @@ constructor(
private val _clockPosition = MutableStateFlow(Position(0, 0)) private val _clockPosition = MutableStateFlow(Position(0, 0))
override val clockPosition = _clockPosition.asStateFlow() override val clockPosition = _clockPosition.asStateFlow()
override val isKeyguardShowing: Flow<Boolean> = conflatedCallbackFlow { override val isKeyguardShowing: Flow<Boolean> =
val callback = conflatedCallbackFlow {
object : KeyguardStateController.Callback { val callback =
override fun onKeyguardShowingChanged() { object : KeyguardStateController.Callback {
trySendWithFailureLogging( override fun onKeyguardShowingChanged() {
keyguardStateController.isShowing, trySendWithFailureLogging(
TAG, keyguardStateController.isShowing,
"updated isKeyguardShowing" TAG,
) "updated isKeyguardShowing"
} )
}
}
keyguardStateController.addCallback(callback)
// Adding the callback does not send an initial update.
trySendWithFailureLogging(
keyguardStateController.isShowing,
TAG,
"initial isKeyguardShowing"
)
awaitClose { keyguardStateController.removeCallback(callback) }
} }
.distinctUntilChanged()
keyguardStateController.addCallback(callback) override val isKeyguardOccluded: Flow<Boolean> =
// Adding the callback does not send an initial update. conflatedCallbackFlow {
trySendWithFailureLogging( val callback =
keyguardStateController.isShowing, object : KeyguardStateController.Callback {
TAG, override fun onKeyguardShowingChanged() {
"initial isKeyguardShowing" trySendWithFailureLogging(
) keyguardStateController.isOccluded,
TAG,
"updated isKeyguardOccluded"
)
}
}
awaitClose { keyguardStateController.removeCallback(callback) } keyguardStateController.addCallback(callback)
} // Adding the callback does not send an initial update.
trySendWithFailureLogging(
keyguardStateController.isOccluded,
TAG,
"initial isKeyguardOccluded"
)
awaitClose { keyguardStateController.removeCallback(callback) }
}
.distinctUntilChanged()
override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow {
val callback = val callback =
@@ -279,36 +310,45 @@ constructor(
} }
.distinctUntilChanged() .distinctUntilChanged()
override val isDreamingWithOverlay: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
object : DreamOverlayCallbackController.Callback {
override fun onStartDream() {
trySendWithFailureLogging(true, TAG, "updated isDreamingWithOverlay")
}
override fun onWakeUp() {
trySendWithFailureLogging(false, TAG, "updated isDreamingWithOverlay")
}
}
dreamOverlayCallbackController.addCallback(callback)
trySendWithFailureLogging(
dreamOverlayCallbackController.isDreaming,
TAG,
"initial isDreamingWithOverlay",
)
awaitClose { dreamOverlayCallbackController.removeCallback(callback) }
}
.distinctUntilChanged()
override val isDreaming: Flow<Boolean> = override val isDreaming: Flow<Boolean> =
merge( conflatedCallbackFlow {
conflatedCallbackFlow { val callback =
val callback = object : KeyguardUpdateMonitorCallback() {
object : KeyguardUpdateMonitorCallback() { override fun onDreamingStateChanged(isDreaming: Boolean) {
override fun onDreamingStateChanged(isDreaming: Boolean) { trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
}
} }
keyguardUpdateMonitor.registerCallback(callback) }
trySendWithFailureLogging( keyguardUpdateMonitor.registerCallback(callback)
keyguardUpdateMonitor.isDreaming, trySendWithFailureLogging(
TAG, keyguardUpdateMonitor.isDreaming,
"initial isDreaming", TAG,
) "initial isDreaming",
)
awaitClose { keyguardUpdateMonitor.removeCallback(callback) } awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
}, }
conflatedCallbackFlow {
val callback =
object : DreamCallback {
override fun onWakeUp() {
trySendWithFailureLogging(false, TAG, "updated isDreaming")
}
}
dreamCallbackController.addCallback(callback)
awaitClose { dreamCallbackController.removeCallback(callback) }
}
)
.distinctUntilChanged() .distinctUntilChanged()
override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow { override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {

View File

@@ -131,6 +131,10 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
} }
override fun startTransition(info: TransitionInfo): UUID? { override fun startTransition(info: TransitionInfo): UUID? {
if (lastStep.from == info.from && lastStep.to == info.to) {
Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
return null
}
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")
} }

View File

@@ -21,6 +21,7 @@ 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.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeStateModel
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
@@ -30,33 +31,33 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class AodLockscreenTransitionInteractor class FromAodTransitionInteractor
@Inject @Inject
constructor( 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,
) : TransitionInteractor(AodLockscreenTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromAodTransitionInteractor::class.simpleName!!) {
override fun start() { override fun start() {
listenForTransitionToAodFromLockscreen() listenForAodToLockscreen()
listenForTransitionToLockscreenFromDozeStates() listenForAodToGone()
} }
private fun listenForTransitionToAodFromLockscreen() { private fun listenForAodToLockscreen() {
scope.launch { scope.launch {
keyguardInteractor keyguardInteractor
.dozeTransitionTo(DozeStateModel.DOZE_AOD) .dozeTransitionTo(DozeStateModel.FINISH)
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair -> .collect { pair ->
val (dozeToAod, lastStartedStep) = pair val (dozeToAod, lastStartedStep) = pair
if (lastStartedStep.to == KeyguardState.LOCKSCREEN) { if (lastStartedStep.to == KeyguardState.AOD) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,
KeyguardState.LOCKSCREEN,
KeyguardState.AOD, KeyguardState.AOD,
KeyguardState.LOCKSCREEN,
getAnimator(), getAnimator(),
) )
) )
@@ -65,20 +66,20 @@ constructor(
} }
} }
private fun listenForTransitionToLockscreenFromDozeStates() { private fun listenForAodToGone() {
val canGoToLockscreen = setOf(KeyguardState.AOD, KeyguardState.DOZING)
scope.launch { scope.launch {
keyguardInteractor keyguardInteractor.biometricUnlockState
.dozeTransitionTo(DozeStateModel.FINISH) .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair -> .collect { pair ->
val (dozeToAod, lastStartedStep) = pair val (biometricUnlockState, keyguardState) = pair
if (canGoToLockscreen.contains(lastStartedStep.to)) { if (
keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)
) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,
lastStartedStep.to, KeyguardState.AOD,
KeyguardState.LOCKSCREEN, KeyguardState.GONE,
getAnimator(), getAnimator(),
) )
) )

View File

@@ -23,16 +23,18 @@ 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.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample import com.android.systemui.util.kotlin.sample
import java.util.UUID import java.util.UUID
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
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class BouncerToGoneTransitionInteractor class FromBouncerTransitionInteractor
@Inject @Inject
constructor( constructor(
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
@@ -40,15 +42,54 @@ constructor(
private val shadeRepository: ShadeRepository, private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor private val keyguardTransitionInteractor: KeyguardTransitionInteractor
) : TransitionInteractor(BouncerToGoneTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromBouncerTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null private var transitionId: UUID? = null
override fun start() { override fun start() {
listenForKeyguardGoingAway() listenForBouncerToGone()
listenForBouncerToLockscreenOrAod()
} }
private fun listenForKeyguardGoingAway() { private fun listenForBouncerToLockscreenOrAod() {
scope.launch {
keyguardInteractor.isBouncerShowing
.sample(
combine(
keyguardInteractor.wakefulnessModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
::Pair
),
::toTriple
)
.collect { triple ->
val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
val to =
if (
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
wakefulnessState.state == WakefulnessState.ASLEEP
) {
KeyguardState.AOD
} else {
KeyguardState.LOCKSCREEN
}
keyguardTransitionRepository.startTransition(
TransitionInfo(
ownerName = name,
from = KeyguardState.BOUNCER,
to = to,
animator = getAnimator(),
)
)
}
}
}
}
private fun listenForBouncerToGone() {
scope.launch { scope.launch {
keyguardInteractor.isKeyguardGoingAway keyguardInteractor.isKeyguardGoingAway
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })

View File

@@ -21,36 +21,46 @@ 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.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
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 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.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class LockscreenGoneTransitionInteractor class FromDozingTransitionInteractor
@Inject @Inject
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(LockscreenGoneTransitionInteractor::class.simpleName!!) { private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor(FromDozingTransitionInteractor::class.simpleName!!) {
override fun start() { override fun start() {
listenForDozingToLockscreen()
}
private fun listenForDozingToLockscreen() {
scope.launch { scope.launch {
keyguardInteractor.isKeyguardGoingAway keyguardInteractor.dozeTransitionModel
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair) .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair -> .collect { pair ->
val (isKeyguardGoingAway, lastStartedStep) = pair val (dozeTransitionModel, lastStartedTransition) = pair
if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) { if (
isDozeOff(dozeTransitionModel.to) &&
lastStartedTransition.to == KeyguardState.DOZING
) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,
KeyguardState.DOZING,
KeyguardState.LOCKSCREEN, KeyguardState.LOCKSCREEN,
KeyguardState.GONE,
getAnimator(), getAnimator(),
) )
) )
@@ -59,14 +69,14 @@ constructor(
} }
} }
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 = 10L private val DEFAULT_DURATION = 500.milliseconds
} }
} }

View File

@@ -36,75 +36,40 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class DreamingTransitionInteractor class FromDreamingTransitionInteractor
@Inject @Inject
constructor( 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,
) : TransitionInteractor(DreamingTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromDreamingTransitionInteractor::class.simpleName!!) {
private val canDreamFrom =
setOf(KeyguardState.LOCKSCREEN, KeyguardState.GONE, KeyguardState.DOZING)
override fun start() { override fun start() {
listenForEntryToDreaming()
listenForDreamingToLockscreen() listenForDreamingToLockscreen()
listenForDreamingToOccluded()
listenForDreamingToGone() listenForDreamingToGone()
listenForDreamingToDozing() listenForDreamingToDozing()
} }
private fun listenForEntryToDreaming() {
scope.launch {
keyguardInteractor.isDreaming
.sample(
combine(
keyguardInteractor.dozeTransitionModel,
keyguardTransitionInteractor.finishedKeyguardState,
::Pair
),
::toTriple
)
.collect { triple ->
val (isDreaming, dozeTransitionModel, keyguardState) = triple
// Dozing/AOD and dreaming have overlapping events. If the state remains in
// FINISH, it means that doze mode is not running and DREAMING is ok to
// commence.
if (
isDozeOff(dozeTransitionModel.to) &&
isDreaming &&
canDreamFrom.contains(keyguardState)
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
keyguardState,
KeyguardState.DREAMING,
getAnimator(),
)
)
}
}
}
}
private fun listenForDreamingToLockscreen() { private fun listenForDreamingToLockscreen() {
scope.launch { scope.launch {
keyguardInteractor.isDreaming // Using isDreamingWithOverlay provides an optimized path to LOCKSCREEN state, which
// otherwise would have gone through OCCLUDED first
keyguardInteractor.isDreamingWithOverlay
.sample( .sample(
combine( combine(
keyguardInteractor.dozeTransitionModel, keyguardInteractor.dozeTransitionModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep, keyguardTransitionInteractor.startedKeyguardTransitionStep,
::Pair, ::Pair
), ),
::toTriple ::toTriple
) )
.collect { triple -> .collect { triple ->
val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple val (isDreaming, dozeTransitionModel, lastStartedTransition) = triple
if ( if (
isDozeOff(dozeTransitionModel.to) && !isDreaming &&
!isDreaming && isDozeOff(dozeTransitionModel.to) &&
lastStartedTransition.to == KeyguardState.DREAMING lastStartedTransition.to == KeyguardState.DREAMING
) { ) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
@@ -120,6 +85,42 @@ constructor(
} }
} }
private fun listenForDreamingToOccluded() {
scope.launch {
keyguardInteractor.isDreaming
.sample(
combine(
keyguardInteractor.isKeyguardOccluded,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
::Pair,
),
::toTriple
)
.collect { triple ->
val (isDreaming, isOccluded, lastStartedTransition) = triple
if (
isOccluded &&
!isDreaming &&
(lastStartedTransition.to == KeyguardState.DREAMING ||
lastStartedTransition.to == KeyguardState.LOCKSCREEN)
) {
// At the moment, checking for LOCKSCREEN state above provides a corrective
// action. There's no great signal to determine when the dream is ending
// and a transition to OCCLUDED is beginning directly. For now, the solution
// is DREAMING->LOCKSCREEN->OCCLUDED
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
lastStartedTransition.to,
KeyguardState.OCCLUDED,
getAnimator(),
)
)
}
}
}
}
private fun listenForDreamingToGone() { private fun listenForDreamingToGone() {
scope.launch { scope.launch {
keyguardInteractor.biometricUnlockState keyguardInteractor.biometricUnlockState

View File

@@ -30,19 +30,44 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class GoneAodTransitionInteractor class FromGoneTransitionInteractor
@Inject @Inject
constructor( 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,
) : TransitionInteractor(GoneAodTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromGoneTransitionInteractor::class.simpleName!!) {
override fun start() { override fun start() {
listenForGoneToAod()
listenForGoneToDreaming()
}
private fun listenForGoneToDreaming() {
scope.launch {
keyguardInteractor.isAbleToDream
.sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
.collect { pair ->
val (isAbleToDream, keyguardState) = pair
if (isAbleToDream && keyguardState == KeyguardState.GONE) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.GONE,
KeyguardState.DREAMING,
getAnimator(),
)
)
}
}
}
}
private fun listenForGoneToAod() {
scope.launch { scope.launch {
keyguardInteractor.wakefulnessModel keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
.collect { pair -> .collect { pair ->
val (wakefulnessState, keyguardState) = pair val (wakefulnessState, keyguardState) = pair
if ( if (

View File

@@ -21,11 +21,11 @@ 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.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
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
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample import com.android.systemui.util.kotlin.sample
import java.util.UUID import java.util.UUID
@@ -36,57 +36,54 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class LockscreenBouncerTransitionInteractor class FromLockscreenTransitionInteractor
@Inject @Inject
constructor( constructor(
@Application private val scope: CoroutineScope, @Application private val scope: CoroutineScope,
private val keyguardInteractor: KeyguardInteractor, private val keyguardInteractor: KeyguardInteractor,
private val shadeRepository: ShadeRepository, private val shadeRepository: ShadeRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository, private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor ) : TransitionInteractor(FromLockscreenTransitionInteractor::class.simpleName!!) {
) : TransitionInteractor(LockscreenBouncerTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null private var transitionId: UUID? = null
override fun start() { override fun start() {
listenForDraggingUpToBouncer() listenForLockscreenToGone()
listenForBouncer() listenForLockscreenToOccluded()
listenForLockscreenToAod()
listenForLockscreenToBouncer()
listenForLockscreenToDreaming()
listenForLockscreenToBouncerDragging()
} }
private fun listenForBouncer() { private fun listenForLockscreenToDreaming() {
scope.launch { scope.launch {
keyguardInteractor.isBouncerShowing keyguardInteractor.isAbleToDream
.sample( .sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
combine( .collect { pair ->
keyguardInteractor.wakefulnessModel, val (isAbleToDream, lastStartedTransition) = pair
keyguardTransitionInteractor.startedKeyguardTransitionStep, if (isAbleToDream && lastStartedTransition.to == KeyguardState.LOCKSCREEN) {
::Pair
),
::toTriple
)
.collect { triple ->
val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
val to =
if (
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
wakefulnessState.state == WakefulnessState.ASLEEP
) {
KeyguardState.AOD
} else {
KeyguardState.LOCKSCREEN
}
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
ownerName = name, name,
from = KeyguardState.BOUNCER, KeyguardState.LOCKSCREEN,
to = to, KeyguardState.DREAMING,
animator = getAnimator(), getAnimator(),
) )
) )
} else if ( }
}
}
}
private fun listenForLockscreenToBouncer() {
scope.launch {
keyguardInteractor.isBouncerShowing
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isBouncerShowing, lastStartedTransitionStep) = pair
if (
isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.LOCKSCREEN
) { ) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
@@ -98,13 +95,12 @@ constructor(
) )
) )
} }
Unit
} }
} }
} }
/* Starts transitions when manually dragging up the bouncer from the lockscreen. */ /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
private fun listenForDraggingUpToBouncer() { private fun listenForLockscreenToBouncerDragging() {
scope.launch { scope.launch {
shadeRepository.shadeModel shadeRepository.shadeModel
.sample( .sample(
@@ -157,6 +153,76 @@ constructor(
} }
} }
private fun listenForLockscreenToGone() {
scope.launch {
keyguardInteractor.isKeyguardGoingAway
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isKeyguardGoingAway, lastStartedStep) = pair
if (isKeyguardGoingAway && lastStartedStep.to == KeyguardState.LOCKSCREEN) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.LOCKSCREEN,
KeyguardState.GONE,
getAnimator(),
)
)
}
}
}
}
private fun listenForLockscreenToOccluded() {
scope.launch {
keyguardInteractor.isKeyguardOccluded
.sample(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.isDreaming,
::Pair
),
::toTriple
)
.collect { triple ->
val (isOccluded, keyguardState, isDreaming) = triple
// Occlusion signals come from the framework, and should interrupt any
// existing transition
if (isOccluded && !isDreaming) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
keyguardState,
KeyguardState.OCCLUDED,
getAnimator(),
)
)
}
}
}
}
private fun listenForLockscreenToAod() {
scope.launch {
keyguardInteractor
.dozeTransitionTo(DozeStateModel.DOZE_AOD)
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (dozeToAod, lastStartedStep) = pair
if (lastStartedStep.to == KeyguardState.LOCKSCREEN) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.LOCKSCREEN,
KeyguardState.AOD,
getAnimator(),
)
)
}
}
}
}
private fun getAnimator(): ValueAnimator { private fun getAnimator(): ValueAnimator {
return ValueAnimator().apply { return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR) setInterpolator(Interpolators.LINEAR)
@@ -165,6 +231,6 @@ constructor(
} }
companion object { companion object {
private const val TRANSITION_DURATION_MS = 300L private const val TRANSITION_DURATION_MS = 500L
} }
} }

View File

@@ -21,39 +21,43 @@ 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.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.Companion.isWakeAndUnlock
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 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.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SysUISingleton @SysUISingleton
class AodToGoneTransitionInteractor class FromOccludedTransitionInteractor
@Inject @Inject
constructor( 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,
) : TransitionInteractor(AodToGoneTransitionInteractor::class.simpleName!!) { ) : TransitionInteractor(FromOccludedTransitionInteractor::class.simpleName!!) {
override fun start() { override fun start() {
listenForOccludedToLockscreen()
listenForOccludedToDreaming()
}
private fun listenForOccludedToDreaming() {
scope.launch { scope.launch {
keyguardInteractor.biometricUnlockState keyguardInteractor.isAbleToDream
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) }) .sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
.collect { pair -> .collect { pair ->
val (biometricUnlockState, keyguardState) = pair val (isAbleToDream, keyguardState) = pair
if ( if (isAbleToDream && keyguardState == KeyguardState.OCCLUDED) {
keyguardState == KeyguardState.AOD && isWakeAndUnlock(biometricUnlockState)
) {
keyguardTransitionRepository.startTransition( keyguardTransitionRepository.startTransition(
TransitionInfo( TransitionInfo(
name, name,
KeyguardState.AOD, KeyguardState.OCCLUDED,
KeyguardState.GONE, KeyguardState.DREAMING,
getAnimator(), getAnimator(),
) )
) )
@@ -62,14 +66,37 @@ constructor(
} }
} }
private fun getAnimator(): ValueAnimator { private fun listenForOccludedToLockscreen() {
scope.launch {
keyguardInteractor.isKeyguardOccluded
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isOccluded, lastStartedKeyguardState) = pair
// Occlusion signals come from the framework, and should interrupt any
// existing transition
if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.OCCLUDED,
KeyguardState.LOCKSCREEN,
getAnimator(),
)
)
}
}
}
}
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 = 500L private val DEFAULT_DURATION = 500.milliseconds
val TO_LOCKSCREEN_DURATION = 1183.milliseconds
} }
} }

View File

@@ -22,12 +22,16 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.DozeStateModel import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
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.util.kotlin.sample
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.merge
/** /**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it. * Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -52,8 +56,27 @@ constructor(
* but not vice-versa. * but not vice-versa.
*/ */
val isDreaming: Flow<Boolean> = repository.isDreaming val isDreaming: Flow<Boolean> = repository.isDreaming
/** Whether the system is dreaming with an overlay active */
val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay
/**
* 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.
*/
val isAbleToDream: Flow<Boolean> =
merge(isDreaming, isDreamingWithOverlay)
.sample(
dozeTransitionModel,
{ isDreaming, dozeTransitionModel ->
isDreaming && isDozeOff(dozeTransitionModel.to)
}
)
.distinctUntilChanged()
/** 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 occluded (covered by an activity). */
val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded
/** Whether the keyguard is going away. */ /** Whether the keyguard is going away. */
val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway
/** Whether the bouncer is showing or not. */ /** Whether the bouncer is showing or not. */
@@ -74,8 +97,8 @@ constructor(
/** The approximate location on the screen of the face unlock sensor, if one is available. */ /** The approximate location on the screen of the face unlock sensor, if one is available. */
val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> { fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> {
return dozeTransitionModel.filter { it.to == state } return dozeTransitionModel.filter { states.contains(it.to) }
} }
fun isKeyguardShowing(): Boolean { fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing() return repository.isKeyguardShowing()

View File

@@ -37,13 +37,13 @@ constructor(
// exhaustive // exhaustive
val ret = val ret =
when (it) { when (it) {
is LockscreenBouncerTransitionInteractor -> Log.d(TAG, "Started $it") is FromBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
is AodLockscreenTransitionInteractor -> Log.d(TAG, "Started $it") is FromAodTransitionInteractor -> Log.d(TAG, "Started $it")
is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it") is FromGoneTransitionInteractor -> Log.d(TAG, "Started $it")
is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it") is FromLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it") is FromDreamingTransitionInteractor -> Log.d(TAG, "Started $it")
is BouncerToGoneTransitionInteractor -> Log.d(TAG, "Started $it") is FromOccludedTransitionInteractor -> Log.d(TAG, "Started $it")
is DreamingTransitionInteractor -> Log.d(TAG, "Started $it") is FromDozingTransitionInteractor -> Log.d(TAG, "Started $it")
} }
it.start() it.start()
} }

View File

@@ -89,6 +89,7 @@ constructor(
KeyguardState.BOUNCER -> true KeyguardState.BOUNCER -> true
KeyguardState.LOCKSCREEN -> true KeyguardState.LOCKSCREEN -> true
KeyguardState.GONE -> true KeyguardState.GONE -> true
KeyguardState.OCCLUDED -> true
} }
} }
} }

View File

@@ -32,25 +32,25 @@ abstract class StartKeyguardTransitionModule {
@Binds @Binds
@IntoSet @IntoSet
abstract fun lockscreenBouncer( abstract fun fromBouncer(impl: FromBouncerTransitionInteractor): TransitionInteractor
impl: LockscreenBouncerTransitionInteractor
): TransitionInteractor
@Binds @Binds
@IntoSet @IntoSet
abstract fun aodLockscreen(impl: AodLockscreenTransitionInteractor): TransitionInteractor abstract fun fromLockscreen(impl: FromLockscreenTransitionInteractor): TransitionInteractor
@Binds @IntoSet abstract fun goneAod(impl: GoneAodTransitionInteractor): TransitionInteractor @Binds @IntoSet abstract fun fromAod(impl: FromAodTransitionInteractor): TransitionInteractor
@Binds @IntoSet abstract fun aodGone(impl: AodToGoneTransitionInteractor): TransitionInteractor @Binds @IntoSet abstract fun fromGone(impl: FromGoneTransitionInteractor): TransitionInteractor
@Binds @Binds
@IntoSet @IntoSet
abstract fun bouncerGone(impl: BouncerToGoneTransitionInteractor): TransitionInteractor abstract fun fromDreaming(impl: FromDreamingTransitionInteractor): TransitionInteractor
@Binds @Binds
@IntoSet @IntoSet
abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor abstract fun fromOccluded(impl: FromOccludedTransitionInteractor): TransitionInteractor
@Binds @IntoSet abstract fun dreaming(impl: DreamingTransitionInteractor): TransitionInteractor @Binds
@IntoSet
abstract fun fromDozing(impl: FromDozingTransitionInteractor): TransitionInteractor
} }

View File

@@ -31,4 +31,6 @@ sealed class TransitionInteractor(val name: String) {
abstract fun start() abstract fun start()
fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second) fun <A, B, C> toTriple(a: A, bc: Pair<B, C>) = Triple(a, bc.first, bc.second)
fun <A, B, C> toTriple(ab: Pair<A, B>, c: C) = Triple(ab.first, ab.second, c)
} }

View File

@@ -57,4 +57,8 @@ enum class KeyguardState {
* with SWIPE security method or face unlock without bypass. * with SWIPE security method or face unlock without bypass.
*/ */
GONE, GONE,
/*
* An activity is displaying over the keyguard.
*/
OCCLUDED,
} }

View File

@@ -19,7 +19,7 @@ package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams import com.android.systemui.keyguard.shared.model.AnimationParams
import javax.inject.Inject import javax.inject.Inject

View File

@@ -18,6 +18,7 @@ package com.android.systemui.dreams
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@@ -30,23 +31,27 @@ import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
class DreamCallbackControllerTest : SysuiTestCase() { class DreamOverlayCallbackControllerTest : SysuiTestCase() {
@Mock private lateinit var callback: DreamCallbackController.DreamCallback @Mock private lateinit var callback: DreamOverlayCallbackController.Callback
private lateinit var underTest: DreamCallbackController private lateinit var underTest: DreamOverlayCallbackController
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
underTest = DreamCallbackController() underTest = DreamOverlayCallbackController()
} }
@Test @Test
fun testOnWakeUpInvokesCallback() { fun onWakeUpInvokesCallback() {
underTest.onStartDream()
assertThat(underTest.isDreaming).isEqualTo(true)
underTest.addCallback(callback) underTest.addCallback(callback)
underTest.onWakeUp() underTest.onWakeUp()
verify(callback).onWakeUp() verify(callback).onWakeUp()
assertThat(underTest.isDreaming).isEqualTo(false)
// Adding twice should not invoke twice // Adding twice should not invoke twice
reset(callback) reset(callback)
@@ -60,4 +65,27 @@ class DreamCallbackControllerTest : SysuiTestCase() {
underTest.onWakeUp() underTest.onWakeUp()
verify(callback, never()).onWakeUp() verify(callback, never()).onWakeUp()
} }
@Test
fun onStartDreamInvokesCallback() {
underTest.addCallback(callback)
assertThat(underTest.isDreaming).isEqualTo(false)
underTest.onStartDream()
verify(callback).onStartDream()
assertThat(underTest.isDreaming).isEqualTo(true)
// Adding twice should not invoke twice
reset(callback)
underTest.addCallback(callback)
underTest.onStartDream()
verify(callback, times(1)).onStartDream()
// After remove, no call to callback
reset(callback)
underTest.removeCallback(callback)
underTest.onStartDream()
verify(callback, never()).onStartDream()
}
} }

View File

@@ -114,7 +114,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
UiEventLogger mUiEventLogger; UiEventLogger mUiEventLogger;
@Mock @Mock
DreamCallbackController mDreamCallbackController; DreamOverlayCallbackController mDreamOverlayCallbackController;
@Captor @Captor
ArgumentCaptor<View> mViewCaptor; ArgumentCaptor<View> mViewCaptor;
@@ -145,7 +145,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mKeyguardUpdateMonitor, mKeyguardUpdateMonitor,
mUiEventLogger, mUiEventLogger,
LOW_LIGHT_COMPONENT, LOW_LIGHT_COMPONENT,
mDreamCallbackController); mDreamOverlayCallbackController);
} }
@Test @Test
@@ -357,7 +357,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mService.onWakeUp(callback); mService.onWakeUp(callback);
mMainExecutor.runAllReady(); mMainExecutor.runAllReady();
verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor); verify(mDreamOverlayContainerViewController).wakeUp(callback, mMainExecutor);
verify(mDreamCallbackController).onWakeUp(); verify(mDreamOverlayCallbackController).onWakeUp();
} }
@Test @Test

View File

@@ -28,8 +28,7 @@ import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.dreams.DreamCallbackController import com.android.systemui.dreams.DreamOverlayCallbackController
import com.android.systemui.dreams.DreamCallbackController.DreamCallback
import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
@@ -68,7 +67,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
@Mock private lateinit var authController: AuthController @Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamCallbackController: DreamCallbackController @Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
private lateinit var underTest: KeyguardRepositoryImpl private lateinit var underTest: KeyguardRepositoryImpl
@@ -86,7 +85,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
keyguardUpdateMonitor, keyguardUpdateMonitor,
dozeTransitionListener, dozeTransitionListener,
authController, authController,
dreamCallbackController, dreamOverlayCallbackController,
) )
} }
@@ -170,6 +169,29 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun isKeyguardOccluded() =
runTest(UnconfinedTestDispatcher()) {
whenever(keyguardStateController.isOccluded).thenReturn(false)
var latest: Boolean? = null
val job = underTest.isKeyguardOccluded.onEach { latest = it }.launchIn(this)
assertThat(latest).isFalse()
val captor = argumentCaptor<KeyguardStateController.Callback>()
verify(keyguardStateController).addCallback(captor.capture())
whenever(keyguardStateController.isOccluded).thenReturn(true)
captor.value.onKeyguardShowingChanged()
assertThat(latest).isTrue()
whenever(keyguardStateController.isOccluded).thenReturn(false)
captor.value.onKeyguardShowingChanged()
assertThat(latest).isFalse()
job.cancel()
}
@Test @Test
fun isDozing() = fun isDozing() =
runTest(UnconfinedTestDispatcher()) { runTest(UnconfinedTestDispatcher()) {
@@ -343,19 +365,22 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
} }
@Test @Test
fun isDreamingFromDreamCallbackController() = fun isDreamingFromDreamOverlayCallbackController() =
runTest(UnconfinedTestDispatcher()) { runTest(UnconfinedTestDispatcher()) {
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(true) whenever(dreamOverlayCallbackController.isDreaming).thenReturn(false)
var latest: Boolean? = null var latest: Boolean? = null
val job = underTest.isDreaming.onEach { latest = it }.launchIn(this) val job = underTest.isDreamingWithOverlay.onEach { latest = it }.launchIn(this)
assertThat(latest).isTrue() assertThat(latest).isFalse()
val listener = val listener =
withArgCaptor<DreamCallbackController.DreamCallback> { withArgCaptor<DreamOverlayCallbackController.Callback> {
verify(dreamCallbackController).addCallback(capture()) verify(dreamOverlayCallbackController).addCallback(capture())
} }
listener.onStartDream()
assertThat(latest).isTrue()
listener.onWakeUp() listener.onWakeUp()
assertThat(latest).isFalse() assertThat(latest).isFalse()

View File

@@ -23,6 +23,8 @@ import com.android.systemui.animation.Interpolators
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
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.WakeSleepReason import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -42,6 +44,7 @@ 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.Mock import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@@ -64,8 +67,8 @@ 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
private lateinit var lockscreenBouncerTransitionInteractor: private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
LockscreenBouncerTransitionInteractor private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
@Before @Before
fun setUp() { fun setUp() {
@@ -79,17 +82,74 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
transitionRepository = KeyguardTransitionRepositoryImpl() transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository) runner = KeyguardTransitionRunner(transitionRepository)
lockscreenBouncerTransitionInteractor = fromLockscreenTransitionInteractor =
LockscreenBouncerTransitionInteractor( FromLockscreenTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository), keyguardInteractor = KeyguardInteractor(keyguardRepository),
shadeRepository = shadeRepository, shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
) )
lockscreenBouncerTransitionInteractor.start() fromLockscreenTransitionInteractor.start()
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromDreamingTransitionInteractor.start()
} }
@Test
fun `DREAMING to LOCKSCREEN`() =
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 dreaming has stopped
keyguardRepository.setDreamingWithOverlay(false)
// AND occluded has stopped
keyguardRepository.setKeyguardOccluded(false)
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// 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 @Test
fun `LOCKSCREEN to BOUNCER via bouncer showing call`() = fun `LOCKSCREEN to BOUNCER via bouncer showing call`() =
testScope.runTest { testScope.runTest {
@@ -97,7 +157,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardRepository.setWakefulnessModel(startingToWake()) keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent() runCurrent()
// GIVEN a transition has run to LOCKSCREEN // GIVEN a prior transition has run to LOCKSCREEN
runner.startTransition( runner.startTransition(
testScope, testScope,
TransitionInfo( TransitionInfo(
@@ -122,7 +182,7 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
verify(mockTransitionRepository).startTransition(capture()) verify(mockTransitionRepository).startTransition(capture())
} }
// THEN a transition to BOUNCER should occur // THEN a transition to BOUNCER should occur
assertThat(info.ownerName).isEqualTo("LockscreenBouncerTransitionInteractor") assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN) assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.to).isEqualTo(KeyguardState.BOUNCER) assertThat(info.to).isEqualTo(KeyguardState.BOUNCER)
assertThat(info.animator).isNotNull() assertThat(info.animator).isNotNull()

View File

@@ -21,7 +21,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE import com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE
import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE import com.android.systemui.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.DreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION import com.android.systemui.keyguard.domain.interactor.FromDreamingTransitionInteractor.Companion.TO_LOCKSCREEN_DURATION
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AnimationParams import com.android.systemui.keyguard.shared.model.AnimationParams
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState

View File

@@ -46,12 +46,18 @@ 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 _isKeyguardOccluded = MutableStateFlow(false)
override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded
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) private val _isDreaming = MutableStateFlow(false)
override val isDreaming: Flow<Boolean> = _isDreaming override val isDreaming: Flow<Boolean> = _isDreaming
private val _isDreamingWithOverlay = MutableStateFlow(false)
override val isDreamingWithOverlay: Flow<Boolean> = _isDreamingWithOverlay
private val _dozeAmount = MutableStateFlow(0f) private val _dozeAmount = MutableStateFlow(0f)
override val linearDozeAmount: Flow<Float> = _dozeAmount override val linearDozeAmount: Flow<Float> = _dozeAmount
@@ -112,10 +118,18 @@ class FakeKeyguardRepository : KeyguardRepository {
_isKeyguardShowing.value = isShowing _isKeyguardShowing.value = isShowing
} }
fun setKeyguardOccluded(isOccluded: Boolean) {
_isKeyguardOccluded.value = isOccluded
}
fun setDozing(isDozing: Boolean) { fun setDozing(isDozing: Boolean) {
_isDozing.value = isDozing _isDozing.value = isDozing
} }
fun setDreamingWithOverlay(isDreaming: Boolean) {
_isDreamingWithOverlay.value = isDreaming
}
fun setDozeAmount(dozeAmount: Float) { fun setDozeAmount(dozeAmount: Float) {
_dozeAmount.value = dozeAmount _dozeAmount.value = dozeAmount
} }
@@ -144,6 +158,10 @@ class FakeKeyguardRepository : KeyguardRepository {
_fingerprintSensorLocation.tryEmit(location) _fingerprintSensorLocation.tryEmit(location)
} }
fun setDozeTransitionModel(model: DozeTransitionModel) {
_dozeTransitionModel.value = model
}
override fun isUdfpsSupported(): Boolean { override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value return _isUdfpsSupported.value
} }