Transitions - Add dreaming state
Add support for states in and out of dreaming. At the moment, AOD is equivalent to DOZING but these will be separated out in the future. Test: atest KeyguardTransitionRepositoryTest KeyguardRepositoryImplTest Bug: 195430376 Change-Id: I73b51987f01540cc3d321f23286a3344d0847f9c
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.common.shared.model.Position
|
||||
@@ -87,6 +88,14 @@ interface KeyguardRepository {
|
||||
*/
|
||||
val isDozing: Flow<Boolean>
|
||||
|
||||
/**
|
||||
* Observable for whether the device is dreaming.
|
||||
*
|
||||
* Dozing/AOD is a specific type of dream, but it is also possible for other non-systemui dreams
|
||||
* to be active, such as screensavers.
|
||||
*/
|
||||
val isDreaming: Flow<Boolean>
|
||||
|
||||
/**
|
||||
* Observable for the amount of doze we are currently in.
|
||||
*
|
||||
@@ -139,12 +148,12 @@ interface KeyguardRepository {
|
||||
class KeyguardRepositoryImpl
|
||||
@Inject
|
||||
constructor(
|
||||
statusBarStateController: StatusBarStateController,
|
||||
dozeHost: DozeHost,
|
||||
wakefulnessLifecycle: WakefulnessLifecycle,
|
||||
biometricUnlockController: BiometricUnlockController,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||
statusBarStateController: StatusBarStateController,
|
||||
dozeHost: DozeHost,
|
||||
wakefulnessLifecycle: WakefulnessLifecycle,
|
||||
biometricUnlockController: BiometricUnlockController,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
|
||||
) : KeyguardRepository {
|
||||
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
|
||||
override val animateBottomAreaDozingTransitions =
|
||||
@@ -244,6 +253,25 @@ constructor(
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val isDreaming: Flow<Boolean> =
|
||||
conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : KeyguardUpdateMonitorCallback() {
|
||||
override fun onDreamingStateChanged(isDreaming: Boolean) {
|
||||
trySendWithFailureLogging(isDreaming, TAG, "updated isDreaming")
|
||||
}
|
||||
}
|
||||
keyguardUpdateMonitor.registerCallback(callback)
|
||||
trySendWithFailureLogging(
|
||||
keyguardUpdateMonitor.isDreaming,
|
||||
TAG,
|
||||
"initial isDreaming",
|
||||
)
|
||||
|
||||
awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : StatusBarStateController.StateListener {
|
||||
|
||||
@@ -112,7 +112,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
|
||||
// Seed with transitions signaling a boot into lockscreen state
|
||||
emitTransition(
|
||||
TransitionStep(
|
||||
KeyguardState.NONE,
|
||||
KeyguardState.OFF,
|
||||
KeyguardState.LOCKSCREEN,
|
||||
0f,
|
||||
TransitionState.STARTED,
|
||||
@@ -120,7 +120,7 @@ class KeyguardTransitionRepositoryImpl @Inject constructor() : KeyguardTransitio
|
||||
)
|
||||
emitTransition(
|
||||
TransitionStep(
|
||||
KeyguardState.NONE,
|
||||
KeyguardState.OFF,
|
||||
KeyguardState.LOCKSCREEN,
|
||||
1f,
|
||||
TransitionState.FINISHED,
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionInfo
|
||||
import com.android.systemui.util.kotlin.sample
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SysUISingleton
|
||||
class DreamingLockscreenTransitionInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val keyguardInteractor: KeyguardInteractor,
|
||||
private val keyguardTransitionRepository: KeyguardTransitionRepository,
|
||||
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
|
||||
) : TransitionInteractor("DREAMING<->LOCKSCREEN") {
|
||||
|
||||
override fun start() {
|
||||
scope.launch {
|
||||
keyguardInteractor.isDreaming
|
||||
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
|
||||
.collect { pair ->
|
||||
val (isDreaming, keyguardState) = pair
|
||||
if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
|
||||
keyguardTransitionRepository.startTransition(
|
||||
TransitionInfo(
|
||||
name,
|
||||
KeyguardState.LOCKSCREEN,
|
||||
KeyguardState.DREAMING,
|
||||
getAnimator(),
|
||||
)
|
||||
)
|
||||
} else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
|
||||
keyguardTransitionRepository.startTransition(
|
||||
TransitionInfo(
|
||||
name,
|
||||
KeyguardState.DREAMING,
|
||||
KeyguardState.LOCKSCREEN,
|
||||
getAnimator(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAnimator(): ValueAnimator {
|
||||
return ValueAnimator().apply {
|
||||
setInterpolator(Interpolators.LINEAR)
|
||||
setDuration(TRANSITION_DURATION_MS)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TRANSITION_DURATION_MS = 500L
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License
|
||||
*/
|
||||
|
||||
package com.android.systemui.keyguard.domain.interactor
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import com.android.systemui.animation.Interpolators
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
|
||||
import com.android.systemui.keyguard.shared.model.KeyguardState
|
||||
import com.android.systemui.keyguard.shared.model.TransitionInfo
|
||||
import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
|
||||
import com.android.systemui.util.kotlin.sample
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@SysUISingleton
|
||||
class DreamingToAodTransitionInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
@Application private val scope: CoroutineScope,
|
||||
private val keyguardInteractor: KeyguardInteractor,
|
||||
private val keyguardTransitionRepository: KeyguardTransitionRepository,
|
||||
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
|
||||
) : TransitionInteractor("DREAMING->AOD") {
|
||||
|
||||
override fun start() {
|
||||
scope.launch {
|
||||
keyguardInteractor.wakefulnessState
|
||||
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
|
||||
.collect { pair ->
|
||||
val (wakefulnessState, keyguardState) = pair
|
||||
if (
|
||||
isSleepingOrStartingToSleep(wakefulnessState) &&
|
||||
keyguardState == KeyguardState.DREAMING
|
||||
) {
|
||||
keyguardTransitionRepository.startTransition(
|
||||
TransitionInfo(
|
||||
name,
|
||||
KeyguardState.DREAMING,
|
||||
KeyguardState.AOD,
|
||||
getAnimator(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAnimator(): ValueAnimator {
|
||||
return ValueAnimator().apply {
|
||||
setInterpolator(Interpolators.LINEAR)
|
||||
setDuration(TRANSITION_DURATION_MS)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TRANSITION_DURATION_MS = 300L
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,11 @@ constructor(
|
||||
val dozeAmount: Flow<Float> = repository.dozeAmount
|
||||
/** Whether the system is in doze mode. */
|
||||
val isDozing: Flow<Boolean> = repository.isDozing
|
||||
/**
|
||||
* Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
|
||||
* but not vice-versa.
|
||||
*/
|
||||
val isDreaming: Flow<Boolean> = repository.isDreaming
|
||||
/** Whether the keyguard is showing or not. */
|
||||
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
|
||||
/** Whether the keyguard is going away. */
|
||||
|
||||
@@ -43,6 +43,8 @@ constructor(
|
||||
is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
|
||||
is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
|
||||
is BouncerToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
|
||||
is DreamingLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
|
||||
is DreamingToAodTransitionInteractor -> Log.d(TAG, "Started $it")
|
||||
}
|
||||
it.start()
|
||||
}
|
||||
|
||||
@@ -51,4 +51,14 @@ abstract class StartKeyguardTransitionModule {
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun dreamingLockscreen(
|
||||
impl: DreamingLockscreenTransitionInteractor
|
||||
): TransitionInteractor
|
||||
|
||||
@Binds
|
||||
@IntoSet
|
||||
abstract fun dreamingToAod(impl: DreamingToAodTransitionInteractor): TransitionInteractor
|
||||
}
|
||||
|
||||
@@ -17,12 +17,29 @@ package com.android.systemui.keyguard.shared.model
|
||||
|
||||
/** List of all possible states to transition to/from */
|
||||
enum class KeyguardState {
|
||||
/**
|
||||
* For initialization as well as when the security method is set to NONE, indicating that
|
||||
* the keyguard should never be shown.
|
||||
/*
|
||||
* The display is completely off, as well as any sensors that would trigger the device to wake
|
||||
* up.
|
||||
*/
|
||||
OFF,
|
||||
/**
|
||||
* The device has entered a special low-power mode within SystemUI. Doze is technically a
|
||||
* special dream service implementation. No UI is visible. In this state, a least some
|
||||
* low-powered sensors such as lift to wake or tap to wake are enabled, or wake screen for
|
||||
* notifications is enabled, allowing the device to quickly wake up.
|
||||
*/
|
||||
DOZING,
|
||||
/*
|
||||
* A device state after the device times out, which can be from both LOCKSCREEN or GONE states.
|
||||
* DOZING is an example of special version of this state. Dreams may be implemented by third
|
||||
* parties to present their own UI over keyguard, like a screensaver.
|
||||
*/
|
||||
DREAMING,
|
||||
/**
|
||||
* The device has entered a special low-power mode within SystemUI, also called the Always-on
|
||||
* Display (AOD). A minimal UI is presented to show critical information. If the device is in
|
||||
* low-power mode without a UI, then it is DOZING.
|
||||
*/
|
||||
NONE,
|
||||
/* Always-on Display. The device is in a low-power mode with a minimal UI visible */
|
||||
AOD,
|
||||
/*
|
||||
* The security screen prompt UI, containing PIN, Password, Pattern, and all FPS
|
||||
@@ -34,7 +51,6 @@ enum class KeyguardState {
|
||||
* unlocked if SWIPE security method is used, or if face lockscreen bypass is false.
|
||||
*/
|
||||
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
|
||||
|
||||
@@ -17,8 +17,8 @@ package com.android.systemui.keyguard.shared.model
|
||||
|
||||
/** This information will flow from the [KeyguardTransitionRepository] to control the UI layer */
|
||||
data class TransitionStep(
|
||||
val from: KeyguardState = KeyguardState.NONE,
|
||||
val to: KeyguardState = KeyguardState.NONE,
|
||||
val from: KeyguardState = KeyguardState.OFF,
|
||||
val to: KeyguardState = KeyguardState.OFF,
|
||||
val value: Float = 0f, // constrained [0.0, 1.0]
|
||||
val transitionState: TransitionState = TransitionState.FINISHED,
|
||||
val ownerName: String = "",
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data.repository
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.keyguard.KeyguardUpdateMonitor
|
||||
import com.android.keyguard.KeyguardUpdateMonitorCallback
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.common.shared.model.Position
|
||||
import com.android.systemui.doze.DozeHost
|
||||
@@ -60,12 +61,12 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
||||
|
||||
underTest =
|
||||
KeyguardRepositoryImpl(
|
||||
statusBarStateController,
|
||||
dozeHost,
|
||||
wakefulnessLifecycle,
|
||||
biometricUnlockController,
|
||||
keyguardStateController,
|
||||
keyguardUpdateMonitor,
|
||||
statusBarStateController,
|
||||
dozeHost,
|
||||
wakefulnessLifecycle,
|
||||
biometricUnlockController,
|
||||
keyguardStateController,
|
||||
keyguardUpdateMonitor,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -278,6 +279,26 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isDreaming() = runBlockingTest {
|
||||
whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
|
||||
verify(keyguardUpdateMonitor).registerCallback(captor.capture())
|
||||
|
||||
captor.value.onDreamingStateChanged(true)
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
captor.value.onDreamingStateChanged(false)
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun biometricUnlockState() = runBlockingTest {
|
||||
val values = mutableListOf<BiometricUnlockModel>()
|
||||
|
||||
@@ -44,6 +44,9 @@ class FakeKeyguardRepository : KeyguardRepository {
|
||||
private val _isDozing = MutableStateFlow(false)
|
||||
override val isDozing: Flow<Boolean> = _isDozing
|
||||
|
||||
private val _isDreaming = MutableStateFlow(false)
|
||||
override val isDreaming: Flow<Boolean> = _isDreaming
|
||||
|
||||
private val _dozeAmount = MutableStateFlow(0f)
|
||||
override val dozeAmount: Flow<Float> = _dozeAmount
|
||||
|
||||
@@ -54,7 +57,7 @@ class FakeKeyguardRepository : KeyguardRepository {
|
||||
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
|
||||
|
||||
private val _isUdfpsSupported = MutableStateFlow(false)
|
||||
|
||||
|
||||
private val _isBouncerShowing = MutableStateFlow(false)
|
||||
override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
|
||||
|
||||
|
||||
Reference in New Issue
Block a user