Merge "Transitions - Add GONE state" into tm-qpr-dev
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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" })
|
||||
|
||||
@@ -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)
|
||||
|
||||
// 300 - power menu
|
||||
// TODO(b/254512600): Tracking Bug
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -22,4 +22,9 @@ import dagger.Module
|
||||
@Module
|
||||
interface KeyguardRepositoryModule {
|
||||
@Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
|
||||
|
||||
@Binds
|
||||
fun keyguardTransitionRepository(
|
||||
impl: KeyguardTransitionRepositoryImpl
|
||||
): KeyguardTransitionRepository
|
||||
}
|
||||
|
||||
@@ -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].
|
||||
/**
|
||||
* 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 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]
|
||||
*/
|
||||
private var updateTransitionId: UUID? = null
|
||||
val transitions: Flow<TransitionStep>
|
||||
|
||||
/**
|
||||
* Interactors that require information about changes between [KeyguardState]s will call this to
|
||||
@@ -60,65 +66,10 @@ 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? {
|
||||
if (currentTransitionInfo != null) {
|
||||
// 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")
|
||||
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)
|
||||
val updateListener =
|
||||
object : AnimatorUpdateListener {
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
emitTransition(
|
||||
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)
|
||||
}
|
||||
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)
|
||||
animator.removeListener(this)
|
||||
animator.removeUpdateListener(updateListener)
|
||||
}
|
||||
}
|
||||
animator.addListener(adapter)
|
||||
animator.addUpdateListener(updateListener)
|
||||
animator.start()
|
||||
return@startTransition null
|
||||
}
|
||||
?: run {
|
||||
Log.i(TAG, "Starting transition (manual): $info")
|
||||
emitTransition(info, 0f, TransitionState.STARTED)
|
||||
|
||||
// No animator, so it's manual. Provide a mechanism to callback
|
||||
updateTransitionId = UUID.randomUUID()
|
||||
return@startTransition updateTransitionId
|
||||
}
|
||||
}
|
||||
fun startTransition(info: TransitionInfo): UUID?
|
||||
|
||||
/**
|
||||
* Allows manual control of a transition. When calling [startTransition], the consumer must pass
|
||||
@@ -132,58 +83,127 @@ class KeyguardTransitionRepository @Inject constructor() {
|
||||
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]
|
||||
*/
|
||||
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: $lastStep")
|
||||
return null
|
||||
}
|
||||
|
||||
info.animator?.let { animator ->
|
||||
// An animator was provided, so use it to run the transition
|
||||
animator.setFloatValues(0f, 1f)
|
||||
val updateListener =
|
||||
object : AnimatorUpdateListener {
|
||||
override fun onAnimationUpdate(animation: ValueAnimator) {
|
||||
emitTransition(
|
||||
TransitionStep(
|
||||
info,
|
||||
(animation.getAnimatedValue() as Float),
|
||||
TransitionState.RUNNING
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
val adapter =
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationStart(animation: Animator) {
|
||||
emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
|
||||
}
|
||||
override fun onAnimationCancel(animation: Animator) {
|
||||
Log.i(TAG, "Cancelling transition: $info")
|
||||
}
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
emitTransition(TransitionStep(info, 1f, TransitionState.FINISHED))
|
||||
animator.removeListener(this)
|
||||
animator.removeUpdateListener(updateListener)
|
||||
}
|
||||
}
|
||||
animator.addListener(adapter)
|
||||
animator.addUpdateListener(updateListener)
|
||||
animator.start()
|
||||
return@startTransition null
|
||||
}
|
||||
?: run {
|
||||
emitTransition(TransitionStep(info, 0f, TransitionState.STARTED))
|
||||
|
||||
// No animator, so it's manual. Provide a mechanism to callback
|
||||
updateTransitionId = UUID.randomUUID()
|
||||
return@startTransition updateTransitionId
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateTransition(
|
||||
transitionId: UUID,
|
||||
@FloatRange(from = 0.0, to = 1.0) value: Float,
|
||||
state: TransitionState
|
||||
) {
|
||||
if (updateTransitionId != transitionId) {
|
||||
Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user