summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt25
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt162
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt51
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt28
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt149
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt47
25 files changed, 762 insertions, 143 deletions
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index da1d233949cf..3961438ff591 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -237,6 +237,9 @@ class DefaultClockController(
) {
var isActive: Boolean = fraction < 0.5f
fun update(newFraction: Float): Pair<Boolean, Boolean> {
+ if (newFraction == fraction) {
+ return Pair(isActive, false)
+ }
val wasActive = isActive
val hasJumped =
(fraction == 0f && newFraction == 1f) || (fraction == 1f && newFraction == 0f)
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 71e04468c07a..d48d7ffcd824 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -34,6 +34,7 @@ import com.android.systemui.flags.Flags.DOZING_MIGRATION_1
import com.android.systemui.flags.Flags.REGION_SAMPLING
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.dagger.KeyguardClockLog
import com.android.systemui.plugins.ClockController
@@ -47,6 +48,7 @@ import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.util.Locale
@@ -186,8 +188,10 @@ open class ClockEventController @Inject constructor(
private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible
- if (!isKeyguardVisible) {
- clock?.animations?.doze(if (isDozing) 1f else 0f)
+ if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ if (!isKeyguardVisible) {
+ clock?.animations?.doze(if (isDozing) 1f else 0f)
+ }
}
}
@@ -224,6 +228,7 @@ open class ClockEventController @Inject constructor(
listenForDozing(this)
if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
listenForDozeAmountTransition(this)
+ listenForGoneToAodTransition(this)
} else {
listenForDozeAmount(this)
}
@@ -276,6 +281,22 @@ open class ClockEventController @Inject constructor(
}
}
+ /**
+ * When keyguard is displayed again after being gone, the clock must be reset to full
+ * dozing.
+ */
+ @VisibleForTesting
+ internal fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
+ return scope.launch {
+ keyguardTransitionInteractor.goneToAodTransition.filter {
+ it.transitionState == TransitionState.STARTED
+ }.collect {
+ dozeAmount = 1f
+ clock?.animations?.doze(dozeAmount)
+ }
+ }
+ }
+
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 46f3d4e5f6aa..32ce537ea25a 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -21,6 +21,7 @@ import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel
import com.android.systemui.plugins.log.LogLevel.DEBUG
import com.android.systemui.plugins.log.LogLevel.ERROR
+import com.android.systemui.plugins.log.LogLevel.INFO
import com.android.systemui.plugins.log.LogLevel.VERBOSE
import com.android.systemui.plugins.log.LogLevel.WARNING
import com.android.systemui.plugins.log.MessageInitializer
@@ -50,6 +51,14 @@ class KeyguardLogger @Inject constructor(@KeyguardLog private val buffer: LogBuf
buffer.log(TAG, DEBUG, messageInitializer, messagePrinter)
}
+ fun v(msg: String, arg: Any) {
+ buffer.log(TAG, VERBOSE, { str1 = arg.toString() }, { "$msg: $str1" })
+ }
+
+ fun i(msg: String, arg: Any) {
+ buffer.log(TAG, INFO, { str1 = arg.toString() }, { "$msg: $str1" })
+ }
+
// TODO: remove after b/237743330 is fixed
fun logStatusBarCalculatedAlpha(alpha: Float) {
debugLog({ double1 = alpha.toDouble() }, { "Calculated new alpha: $double1" })
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index a4d3399eeb5d..40b5b0ac0e7d 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -122,7 +122,8 @@ object Flags {
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
* will occur in stages. This is one stage of many to come.
*/
- @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213, teamfood = true)
+ // TODO(b/255607168): Tracking Bug
+ @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213)
@JvmField val NEW_ELLIPSE_DETECTION = UnreleasedFlag(214)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index b186ae0ceec4..6baaf5f10024 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -21,7 +21,10 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.doze.DozeHost
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
@@ -89,6 +92,9 @@ interface KeyguardRepository {
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState>
+ /** Observable for device wake/sleep state */
+ val wakefulnessState: Flow<WakefulnessModel>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -118,6 +124,7 @@ constructor(
statusBarStateController: StatusBarStateController,
private val keyguardStateController: KeyguardStateController,
dozeHost: DozeHost,
+ wakefulnessLifecycle: WakefulnessLifecycle,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -207,6 +214,40 @@ constructor(
awaitClose { statusBarStateController.removeCallback(callback) }
}
+ override val wakefulnessState: Flow<WakefulnessModel> = conflatedCallbackFlow {
+ val callback =
+ object : WakefulnessLifecycle.Observer {
+ override fun onStartedWakingUp() {
+ trySendWithFailureLogging(
+ WakefulnessModel.STARTING_TO_WAKE,
+ TAG,
+ "Wakefulness: starting to wake"
+ )
+ }
+ override fun onFinishedWakingUp() {
+ trySendWithFailureLogging(WakefulnessModel.AWAKE, TAG, "Wakefulness: awake")
+ }
+ override fun onStartedGoingToSleep() {
+ trySendWithFailureLogging(
+ WakefulnessModel.STARTING_TO_SLEEP,
+ TAG,
+ "Wakefulness: starting to sleep"
+ )
+ }
+ override fun onFinishedGoingToSleep() {
+ trySendWithFailureLogging(WakefulnessModel.ASLEEP, TAG, "Wakefulness: asleep")
+ }
+ }
+ wakefulnessLifecycle.addObserver(callback)
+ trySendWithFailureLogging(
+ wakefulnessIntToObject(wakefulnessLifecycle.getWakefulness()),
+ TAG,
+ "initial wakefulness state"
+ )
+
+ awaitClose { wakefulnessLifecycle.removeObserver(callback) }
+ }
+
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.value = animate
}
@@ -228,6 +269,16 @@ constructor(
}
}
+ private fun wakefulnessIntToObject(@Wakefulness value: Int): WakefulnessModel {
+ return when (value) {
+ 0 -> WakefulnessModel.ASLEEP
+ 1 -> WakefulnessModel.STARTING_TO_WAKE
+ 2 -> WakefulnessModel.AWAKE
+ 3 -> WakefulnessModel.STARTING_TO_SLEEP
+ else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
+ }
+ }
+
companion object {
private const val TAG = "KeyguardRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index d15d7f25bbde..0c725208e22d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -22,4 +22,9 @@ import dagger.Module
@Module
interface KeyguardRepositoryModule {
@Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
+
+ @Binds
+ fun keyguardTransitionRepository(
+ impl: KeyguardTransitionRepositoryImpl
+ ): KeyguardTransitionRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index ab25597b077c..e3d1a27dad2b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -29,27 +29,33 @@ import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import java.util.UUID
import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
-@SysUISingleton
-class KeyguardTransitionRepository @Inject constructor() {
- /*
- * Each transition between [KeyguardState]s will have an associated Flow.
- * In order to collect these events, clients should call [transition].
- */
- private val _transitions = MutableStateFlow(TransitionStep())
- val transitions = _transitions.asStateFlow()
-
- /* Information about the active transition. */
- private var currentTransitionInfo: TransitionInfo? = null
- /*
- * When manual control of the transition is requested, a unique [UUID] is used as the handle
- * to permit calls to [updateTransition]
+/**
+ * The source of truth for all keyguard transitions.
+ *
+ * While the keyguard component is visible, it can undergo a number of transitions between different
+ * UI screens, such as AOD (Always-on Display), Bouncer, and others mentioned in [KeyguardState].
+ * These UI elements should listen to events emitted by [transitions], to ensure a centrally
+ * coordinated experience.
+ *
+ * To create or modify logic that controls when and how transitions get created, look at
+ * [TransitionInteractor]. These interactors will call [startTransition] and [updateTransition] on
+ * this repository.
+ */
+interface KeyguardTransitionRepository {
+ /**
+ * All events regarding transitions, as they start, run, and complete. [TransitionStep#value] is
+ * a float between [0, 1] representing progress towards completion. If this is a user driven
+ * transition, that value may not be a monotonic progression, as the user may swipe in any
+ * direction.
*/
- private var updateTransitionId: UUID? = null
+ val transitions: Flow<TransitionStep>
/**
* Interactors that require information about changes between [KeyguardState]s will call this to
@@ -60,22 +66,56 @@ class KeyguardTransitionRepository @Inject constructor() {
}
/**
- * Begin a transition from one state to another. The [info.from] must match
- * [currentTransitionInfo.to], or the request will be denied. This is enforced to avoid
- * unplanned transitions.
+ * Begin a transition from one state to another. Will not start if another transition is in
+ * progress.
+ */
+ fun startTransition(info: TransitionInfo): UUID?
+
+ /**
+ * Allows manual control of a transition. When calling [startTransition], the consumer must pass
+ * in a null animator. In return, it will get a unique [UUID] that will be validated to allow
+ * further updates.
+ *
+ * When the transition is over, TransitionState.FINISHED must be passed into the [state]
+ * parameter.
+ */
+ fun updateTransition(
+ transitionId: UUID,
+ @FloatRange(from = 0.0, to = 1.0) value: Float,
+ state: TransitionState
+ )
+}
+
+@SysUISingleton
+class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitionRepository {
+ /*
+ * Each transition between [KeyguardState]s will have an associated Flow.
+ * In order to collect these events, clients should call [transition].
+ */
+ private val _transitions =
+ MutableSharedFlow<TransitionStep>(
+ extraBufferCapacity = 10,
+ onBufferOverflow = BufferOverflow.DROP_OLDEST
+ )
+ override val transitions = _transitions.asSharedFlow().distinctUntilChanged()
+ private var lastStep: TransitionStep = TransitionStep()
+
+ /*
+ * When manual control of the transition is requested, a unique [UUID] is used as the handle
+ * to permit calls to [updateTransition]
*/
- fun startTransition(info: TransitionInfo): UUID? {
- if (currentTransitionInfo != null) {
+ private var updateTransitionId: UUID? = null
+
+ override fun startTransition(info: TransitionInfo): UUID? {
+ if (lastStep.transitionState != TransitionState.FINISHED) {
// Open questions:
// * Queue of transitions? buffer of 1?
// * Are transitions cancellable if a new one is triggered?
// * What validation does this need to do?
- Log.wtf(TAG, "Transition still active: $currentTransitionInfo")
+ Log.wtf(TAG, "Transition still active: $lastStep")
return null
}
- currentTransitionInfo?.animator?.cancel()
- currentTransitionInfo = info
info.animator?.let { animator ->
// An animator was provided, so use it to run the transition
animator.setFloatValues(0f, 1f)
@@ -83,24 +123,24 @@ class KeyguardTransitionRepository @Inject constructor() {
object : AnimatorUpdateListener {
override fun onAnimationUpdate(animation: ValueAnimator) {
emitTransition(
- info,
- (animation.getAnimatedValue() as Float),
- TransitionState.RUNNING
+ TransitionStep(
+ info,
+ (animation.getAnimatedValue() as Float),
+ TransitionState.RUNNING
+ )
)
}
}
val adapter =
object : AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator) {
- Log.i(TAG, "Starting transition: $info")
- emitTransition(info, 0f, TransitionState.STARTED)
+ emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
}
override fun onAnimationCancel(animation: Animator) {
Log.i(TAG, "Cancelling transition: $info")
}
override fun onAnimationEnd(animation: Animator) {
- Log.i(TAG, "Ending transition: $info")
- emitTransition(info, 1f, TransitionState.FINISHED)
+ emitTransition(TransitionStep(info, 1f, TransitionState.FINISHED))
animator.removeListener(this)
animator.removeUpdateListener(updateListener)
}
@@ -111,8 +151,7 @@ class KeyguardTransitionRepository @Inject constructor() {
return@startTransition null
}
?: run {
- Log.i(TAG, "Starting transition (manual): $info")
- emitTransition(info, 0f, TransitionState.STARTED)
+ emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
// No animator, so it's manual. Provide a mechanism to callback
updateTransitionId = UUID.randomUUID()
@@ -120,15 +159,7 @@ class KeyguardTransitionRepository @Inject constructor() {
}
}
- /**
- * Allows manual control of a transition. When calling [startTransition], the consumer must pass
- * in a null animator. In return, it will get a unique [UUID] that will be validated to allow
- * further updates.
- *
- * When the transition is over, TransitionState.FINISHED must be passed into the [state]
- * parameter.
- */
- fun updateTransition(
+ override fun updateTransition(
transitionId: UUID,
@FloatRange(from = 0.0, to = 1.0) value: Float,
state: TransitionState
@@ -138,52 +169,41 @@ class KeyguardTransitionRepository @Inject constructor() {
return
}
- if (currentTransitionInfo == null) {
- Log.wtf(TAG, "Attempting to update with null 'currentTransitionInfo'")
- return
+ if (state == TransitionState.FINISHED) {
+ updateTransitionId = null
}
- currentTransitionInfo?.let { info ->
- if (state == TransitionState.FINISHED) {
- updateTransitionId = null
- Log.i(TAG, "Ending transition: $info")
- }
-
- emitTransition(info, value, state)
- }
+ val nextStep = lastStep.copy(value = value, transitionState = state)
+ emitTransition(nextStep, isManual = true)
}
- private fun emitTransition(
- info: TransitionInfo,
- value: Float,
- transitionState: TransitionState
- ) {
- trace(info, transitionState)
-
- if (transitionState == TransitionState.FINISHED) {
- currentTransitionInfo = null
+ private fun emitTransition(nextStep: TransitionStep, isManual: Boolean = false) {
+ trace(nextStep, isManual)
+ val emitted = _transitions.tryEmit(nextStep)
+ if (!emitted) {
+ Log.w(TAG, "Failed to emit next value without suspending")
}
- _transitions.value = TransitionStep(info.from, info.to, value, transitionState)
+ lastStep = nextStep
}
- private fun trace(info: TransitionInfo, transitionState: TransitionState) {
+ private fun trace(step: TransitionStep, isManual: Boolean) {
if (
- transitionState != TransitionState.STARTED &&
- transitionState != TransitionState.FINISHED
+ step.transitionState != TransitionState.STARTED &&
+ step.transitionState != TransitionState.FINISHED
) {
return
}
val traceName =
- "Transition: ${info.from} -> ${info.to} " +
- if (info.animator == null) {
+ "Transition: ${step.from} -> ${step.to} " +
+ if (isManual) {
"(manual)"
} else {
""
}
val traceCookie = traceName.hashCode()
- if (transitionState == TransitionState.STARTED) {
+ if (step.transitionState == TransitionState.STARTED) {
Trace.beginAsyncSection(traceName, traceCookie)
- } else if (transitionState == TransitionState.FINISHED) {
+ } else if (step.transitionState == TransitionState.FINISHED) {
Trace.endAsyncSection(traceName, traceCookie)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 400376663f1a..0aeff7fc69fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
@@ -36,31 +37,35 @@ constructor(
@Application private val scope: CoroutineScope,
private val keyguardRepository: KeyguardRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
) : TransitionInteractor("AOD<->LOCKSCREEN") {
override fun start() {
scope.launch {
- keyguardRepository.isDozing.collect { isDozing ->
- if (isDozing) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.LOCKSCREEN,
- KeyguardState.AOD,
- getAnimator(),
+ keyguardRepository.isDozing
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isDozing, keyguardState) = pair
+ if (isDozing && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.AOD,
+ getAnimator(),
+ )
)
- )
- } else {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.AOD,
- KeyguardState.LOCKSCREEN,
- getAnimator(),
+ } else if (!isDozing && keyguardState == KeyguardState.AOD) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.AOD,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(),
+ )
)
- )
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
new file mode 100644
index 000000000000..0e2a54c57bdb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class GoneAodTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("GONE->AOD") {
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.wakefulnessState
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (wakefulnessState, keyguardState) = pair
+ if (
+ keyguardState == KeyguardState.GONE &&
+ wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP
+ ) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.GONE,
+ KeyguardState.AOD,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 500L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index fc2269c6b01c..03c6a789326e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -40,6 +41,8 @@ constructor(
val isDozing: Flow<Boolean> = repository.isDozing
/** Whether the keyguard is showing to not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ /** The device wake/sleep state */
+ val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
new file mode 100644
index 000000000000..83d94325b9d4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.keyguard.logging.KeyguardLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/** Collect flows of interest for auditing keyguard transitions. */
+@SysUISingleton
+class KeyguardTransitionAuditLogger
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val interactor: KeyguardTransitionInteractor,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val logger: KeyguardLogger,
+) {
+
+ fun start() {
+ scope.launch {
+ keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
+ }
+
+ scope.launch {
+ interactor.finishedKeyguardState.collect { logger.i("Finished transition to", it) }
+ }
+
+ scope.launch {
+ interactor.startedKeyguardState.collect { logger.i("Started transition to", it) }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index b166681433a8..d5ea77b8e729 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -26,6 +26,7 @@ class KeyguardTransitionCoreStartable
@Inject
constructor(
private val interactors: Set<TransitionInteractor>,
+ private val auditLogger: KeyguardTransitionAuditLogger,
) : CoreStartable {
override fun start() {
@@ -38,9 +39,12 @@ constructor(
when (it) {
is LockscreenBouncerTransitionInteractor -> Log.d(TAG, "Started $it")
is AodLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
+ is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it")
+ is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}
+ auditLogger.start()
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index 7409aec57b4c..dffd097a77c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -19,11 +19,15 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
@@ -40,6 +44,9 @@ constructor(
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
+ /** GONE->AOD information. */
+ val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
+
/**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
* Lockscreen (0f).
@@ -49,4 +56,16 @@ constructor(
aodToLockscreenTransition.map { step -> step.copy(value = 1f - step.value) },
lockscreenToAodTransition,
)
+
+ /* The last completed [KeyguardState] transition */
+ val finishedKeyguardState: Flow<KeyguardState> =
+ repository.transitions
+ .filter { step -> step.transitionState == TransitionState.FINISHED }
+ .map { step -> step.to }
+
+ /* The last started [KeyguardState] transition */
+ val startedKeyguardState: Flow<KeyguardState> =
+ repository.transitions
+ .filter { step -> step.transitionState == TransitionState.STARTED }
+ .map { step -> step.to }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3c2a12e3836a..761f3fd9d9f9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -29,6 +29,7 @@ import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -40,59 +41,63 @@ constructor(
private val keyguardRepository: KeyguardRepository,
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor
) : TransitionInteractor("LOCKSCREEN<->BOUNCER") {
private var transitionId: UUID? = null
override fun start() {
scope.launch {
- shadeRepository.shadeModel.sample(
- combine(
- keyguardTransitionRepository.transitions,
- keyguardRepository.statusBarState,
- ) { transitions, statusBarState ->
- Pair(transitions, statusBarState)
- }
- ) { shadeModel, pair ->
- val (transitions, statusBarState) = pair
+ shadeRepository.shadeModel
+ .sample(
+ combine(
+ keyguardTransitionInteractor.finishedKeyguardState,
+ keyguardRepository.statusBarState,
+ ) { keyguardState, statusBarState ->
+ Pair(keyguardState, statusBarState)
+ },
+ { shadeModel, pair -> Triple(shadeModel, pair.first, pair.second) }
+ )
+ .collect { triple ->
+ val (shadeModel, keyguardState, statusBarState) = triple
- val id = transitionId
- if (id != null) {
- // An existing `id` means a transition is started, and calls to
- // `updateTransition` will control it until FINISHED
- keyguardTransitionRepository.updateTransition(
- id,
- shadeModel.expansionAmount,
- if (shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f) {
- transitionId = null
- TransitionState.FINISHED
- } else {
- TransitionState.RUNNING
- }
- )
- } else {
- // TODO (b/251849525): Remove statusbarstate check when that state is integrated
- // into KeyguardTransitionRepository
- val isOnLockscreen =
- transitions.transitionState == TransitionState.FINISHED &&
- transitions.to == KeyguardState.LOCKSCREEN
- if (
- isOnLockscreen &&
- shadeModel.isUserDragging &&
- statusBarState != SHADE_LOCKED
- ) {
- transitionId =
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- ownerName = name,
- from = KeyguardState.LOCKSCREEN,
- to = KeyguardState.BOUNCER,
- animator = null,
+ val id = transitionId
+ if (id != null) {
+ // An existing `id` means a transition is started, and calls to
+ // `updateTransition` will control it until FINISHED
+ keyguardTransitionRepository.updateTransition(
+ id,
+ shadeModel.expansionAmount,
+ if (
+ shadeModel.expansionAmount == 0f || shadeModel.expansionAmount == 1f
+ ) {
+ transitionId = null
+ TransitionState.FINISHED
+ } else {
+ TransitionState.RUNNING
+ }
+ )
+ } else {
+ // TODO (b/251849525): Remove statusbarstate check when that state is
+ // integrated
+ // into KeyguardTransitionRepository
+ if (
+ keyguardState == KeyguardState.LOCKSCREEN &&
+ shadeModel.isUserDragging &&
+ statusBarState != SHADE_LOCKED
+ ) {
+ transitionId =
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.BOUNCER,
+ animator = null,
+ )
)
- )
+ }
}
}
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
new file mode 100644
index 000000000000..6c1adbd68ef2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenGoneTransitionInteractor.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class LockscreenGoneTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+) : TransitionInteractor("LOCKSCREEN->GONE") {
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.isKeyguardShowing.collect { isShowing ->
+ if (!isShowing) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.GONE,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 10L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 74c542c0043f..728bafae2a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -39,4 +39,10 @@ abstract class StartKeyguardTransitionModule {
@Binds
@IntoSet
abstract fun aodLockscreen(impl: AodLockscreenTransitionInteractor): TransitionInteractor
+
+ @Binds @IntoSet abstract fun goneAod(impl: GoneAodTransitionInteractor): TransitionInteractor
+
+ @Binds
+ @IntoSet
+ abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
index f66d5d3650c8..7958033ba017 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardState.kt
@@ -17,7 +17,10 @@ package com.android.systemui.keyguard.shared.model
/** List of all possible states to transition to/from */
enum class KeyguardState {
- /** For initialization only */
+ /**
+ * For initialization as well as when the security method is set to NONE, indicating that
+ * the keyguard should never be shown.
+ */
NONE,
/* Always-on Display. The device is in a low-power mode with a minimal UI visible */
AOD,
@@ -31,4 +34,11 @@ enum class KeyguardState {
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
*/
LOCKSCREEN,
+
+ /*
+ * Keyguard is no longer visible. In most cases the user has just authenticated and keyguard
+ * is being removed, but there are other cases where the user is swiping away keyguard, such as
+ * with SWIPE security method or face unlock without bypass.
+ */
+ GONE,
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
index d8691c17f53d..0e0465bb5207 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionState.kt
@@ -17,7 +17,6 @@ package com.android.systemui.keyguard.shared.model
/** Possible states for a running transition between [State] */
enum class TransitionState {
- NONE,
STARTED,
RUNNING,
FINISHED
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 688ec912aac8..0ca358210813 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -20,5 +20,11 @@ data class TransitionStep(
val from: KeyguardState = KeyguardState.NONE,
val to: KeyguardState = KeyguardState.NONE,
val value: Float = 0f, // constrained [0.0, 1.0]
- val transitionState: TransitionState = TransitionState.NONE,
-)
+ val transitionState: TransitionState = TransitionState.FINISHED,
+) {
+ constructor(
+ info: TransitionInfo,
+ value: Float,
+ transitionState: TransitionState,
+ ) : this(info.from, info.to, value, transitionState)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
new file mode 100644
index 000000000000..64f834d6c5ab
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+/** Model device wakefulness states. */
+enum class WakefulnessModel {
+ /** The device is asleep and not interactive. */
+ ASLEEP,
+ /** Received a signal that the device is beginning to wake up. */
+ STARTING_TO_WAKE,
+ /** Device is now fully awake and interactive. */
+ AWAKE,
+ /** Signal that the device is now going to sleep. */
+ STARTING_TO_SLEEP,
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 7a1568098e4a..b9ab9d37a805 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -20,6 +20,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
@@ -43,6 +45,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
private lateinit var underTest: KeyguardRepositoryImpl
@@ -55,6 +58,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
statusBarStateController,
keyguardStateController,
dozeHost,
+ wakefulnessLifecycle,
)
}
@@ -184,4 +188,33 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
verify(statusBarStateController).removeCallback(captor.value)
}
+
+ @Test
+ fun wakefullness() = runBlockingTest {
+ val values = mutableListOf<WakefulnessModel>()
+ val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
+ verify(wakefulnessLifecycle).addObserver(captor.capture())
+
+ captor.value.onStartedWakingUp()
+ captor.value.onFinishedWakingUp()
+ captor.value.onStartedGoingToSleep()
+ captor.value.onFinishedGoingToSleep()
+
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be ASLEEP
+ WakefulnessModel.ASLEEP,
+ WakefulnessModel.STARTING_TO_WAKE,
+ WakefulnessModel.AWAKE,
+ WakefulnessModel.STARTING_TO_SLEEP,
+ WakefulnessModel.ASLEEP,
+ )
+ )
+
+ job.cancel()
+ verify(wakefulnessLifecycle).removeObserver(captor.value)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 1b34100b1cef..64913c7c28c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -63,7 +63,7 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
@Before
fun setUp() {
- underTest = KeyguardTransitionRepository()
+ underTest = KeyguardTransitionRepositoryImpl()
wtfHandler = WtfHandler()
oldWtfHandler = Log.setWtfHandler(wtfHandler)
}
@@ -174,9 +174,6 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
}
private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) {
- // + 2 accounts for start and finish of automated transition
- assertThat(steps.size).isEqualTo(fractions.size + 2)
-
assertThat(steps[0]).isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED))
fractions.forEachIndexed { index, fraction ->
assertThat(steps[index + 1])
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
new file mode 100644
index 000000000000..0424c28a1c24
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardTransitionInteractorTest : SysuiTestCase() {
+
+ private lateinit var underTest: KeyguardTransitionInteractor
+ private lateinit var repository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ repository = FakeKeyguardTransitionRepository()
+ underTest = KeyguardTransitionInteractor(repository)
+ }
+
+ @Test
+ fun `transition collectors receives only appropriate events`() =
+ runBlocking(IMMEDIATE) {
+ var goneToAodSteps = mutableListOf<TransitionStep>()
+ val job1 =
+ underTest.goneToAodTransition.onEach { goneToAodSteps.add(it) }.launchIn(this)
+
+ var aodToLockscreenSteps = mutableListOf<TransitionStep>()
+ val job2 =
+ underTest.aodToLockscreenTransition
+ .onEach { aodToLockscreenSteps.add(it) }
+ .launchIn(this)
+
+ val steps = mutableListOf<TransitionStep>()
+ steps.add(TransitionStep(AOD, GONE, 0f, STARTED))
+ steps.add(TransitionStep(AOD, GONE, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(GONE, AOD, 0f, STARTED))
+ steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
+ steps.add(TransitionStep(GONE, AOD, 0.2f, RUNNING))
+
+ steps.forEach { repository.sendTransitionStep(it) }
+
+ assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
+ assertThat(goneToAodSteps).isEqualTo(steps.subList(5, 8))
+
+ job1.cancel()
+ job2.cancel()
+ }
+
+ @Test
+ fun dozeAmountTransitionTest() =
+ runBlocking(IMMEDIATE) {
+ var dozeAmountSteps = mutableListOf<TransitionStep>()
+ val job =
+ underTest.dozeAmountTransition.onEach { dozeAmountSteps.add(it) }.launchIn(this)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.8f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+
+ steps.forEach { repository.sendTransitionStep(it) }
+
+ assertThat(dozeAmountSteps.subList(0, 3))
+ .isEqualTo(
+ listOf(
+ steps[0].copy(value = 1f - steps[0].value),
+ steps[1].copy(value = 1f - steps[1].value),
+ steps[2].copy(value = 1f - steps[2].value),
+ )
+ )
+ assertThat(dozeAmountSteps.subList(3, 7)).isEqualTo(steps.subList(3, 7))
+
+ job.cancel()
+ }
+
+ @Test
+ fun keyguardStateTests() =
+ runBlocking(IMMEDIATE) {
+ var finishedSteps = mutableListOf<KeyguardState>()
+ val job1 =
+ underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
+ var startedSteps = mutableListOf<KeyguardState>()
+ val job2 = underTest.startedKeyguardState.onEach { startedSteps.add(it) }.launchIn(this)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach { repository.sendTransitionStep(it) }
+
+ assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
+ assertThat(startedSteps).isEqualTo(listOf(LOCKSCREEN, AOD, GONE))
+
+ job1.cancel()
+ job2.cancel()
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 0c126805fb78..11178db8afd2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard.data.repository
import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -48,6 +49,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
override val statusBarState: Flow<StatusBarState> = _statusBarState
+ private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
+ override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+
override fun isKeyguardShowing(): Boolean {
return _isKeyguardShowing.value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
new file mode 100644
index 000000000000..6c4424470c1c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardTransitionRepository.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.annotation.FloatRange
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import java.util.UUID
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.SharedFlow
+
+/** Fake implementation of [KeyguardTransitionRepository] */
+class FakeKeyguardTransitionRepository : KeyguardTransitionRepository {
+
+ private val _transitions = MutableSharedFlow<TransitionStep>()
+ override val transitions: SharedFlow<TransitionStep> = _transitions
+
+ suspend fun sendTransitionStep(step: TransitionStep) {
+ _transitions.emit(step)
+ }
+
+ override fun startTransition(info: TransitionInfo): UUID? {
+ return null
+ }
+
+ override fun updateTransition(
+ transitionId: UUID,
+ @FloatRange(from = 0.0, to = 1.0) value: Float,
+ state: TransitionState
+ ) = Unit
+}