Merge "Transitions - Better dozing/aod support" into tm-qpr-dev am: c36131f6f3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20844094

Change-Id: I046f5c2bb509617a1bd7bd90db787ef3df562e58
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Matt Pietal
2023-01-11 15:13:18 +00:00
committed by Automerger Merge Worker
13 changed files with 536 additions and 61 deletions

View File

@@ -114,8 +114,6 @@ object Flags {
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
// new BooleanFlag(200, true);
// TODO(b/254512713): Tracking Bug
@JvmField val LOCKSCREEN_ANIMATIONS = releasedFlag(201, "lockscreen_animations")
// TODO(b/254512750): Tracking Bug
val NEW_UNLOCK_SWIPE_ANIMATION = releasedFlag(202, "new_unlock_swipe_animation")

View File

@@ -40,6 +40,7 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
@@ -88,6 +89,9 @@ interface KeyguardRepository {
/** Observable for whether the bouncer is showing. */
val isBouncerShowing: Flow<Boolean>
/** Is the always-on display available to be used? */
val isAodAvailable: Flow<Boolean>
/**
* Observable for whether we are in doze state.
*
@@ -182,6 +186,7 @@ constructor(
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
private val dozeParameters: DozeParameters,
private val authController: AuthController,
private val dreamOverlayCallbackController: DreamOverlayCallbackController,
) : KeyguardRepository {
@@ -220,6 +225,31 @@ constructor(
}
.distinctUntilChanged()
override val isAodAvailable: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
object : DozeParameters.Callback {
override fun onAlwaysOnChange() {
trySendWithFailureLogging(
dozeParameters.getAlwaysOn(),
TAG,
"updated isAodAvailable"
)
}
}
dozeParameters.addCallback(callback)
// Adding the callback does not send an initial update.
trySendWithFailureLogging(
dozeParameters.getAlwaysOn(),
TAG,
"initial isAodAvailable"
)
awaitClose { dozeParameters.removeCallback(callback) }
}
.distinctUntilChanged()
override val isKeyguardOccluded: Flow<Boolean> =
conflatedCallbackFlow {
val callback =

View File

@@ -21,9 +21,9 @@ 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.DozeStateModel.Companion.isDozeOff
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.isWakingOrStartingToWake
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration
@@ -48,12 +48,11 @@ constructor(
private fun listenForDozingToLockscreen() {
scope.launch {
keyguardInteractor.dozeTransitionModel
keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (dozeTransitionModel, lastStartedTransition) = pair
.collect { (wakefulnessModel, lastStartedTransition) ->
if (
isDozeOff(dozeTransitionModel.to) &&
isWakingOrStartingToWake(wakefulnessModel) &&
lastStartedTransition.to == KeyguardState.DOZING
) {
keyguardTransitionRepository.startTransition(

View File

@@ -26,7 +26,10 @@ import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@SysUISingleton
@@ -40,7 +43,7 @@ constructor(
) : TransitionInteractor(FromGoneTransitionInteractor::class.simpleName!!) {
override fun start() {
listenForGoneToAod()
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
}
@@ -56,7 +59,7 @@ constructor(
name,
KeyguardState.GONE,
KeyguardState.DREAMING,
getAnimator(),
getAnimator(TO_DREAMING_DURATION),
)
)
}
@@ -64,12 +67,18 @@ constructor(
}
}
private fun listenForGoneToAod() {
private fun listenForGoneToAodOrDozing() {
scope.launch {
keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, ::Pair)
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
.sample(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.isAodAvailable,
::Pair
),
::toTriple
)
.collect { (wakefulnessState, keyguardState, isAodAvailable) ->
if (
keyguardState == KeyguardState.GONE &&
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
@@ -78,7 +87,11 @@ constructor(
TransitionInfo(
name,
KeyguardState.GONE,
KeyguardState.AOD,
if (isAodAvailable) {
KeyguardState.AOD
} else {
KeyguardState.DOZING
},
getAnimator(),
)
)
@@ -87,14 +100,15 @@ constructor(
}
}
private fun getAnimator(): ValueAnimator {
private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)
setDuration(TRANSITION_DURATION_MS)
setDuration(duration.inWholeMilliseconds)
}
}
companion object {
private const val TRANSITION_DURATION_MS = 500L
private val DEFAULT_DURATION = 500.milliseconds
val TO_DREAMING_DURATION = 933.milliseconds
}
}

View File

@@ -21,11 +21,11 @@ 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.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
import java.util.UUID
@@ -54,7 +54,7 @@ constructor(
listenForLockscreenToGone()
listenForLockscreenToOccluded()
listenForLockscreenToCamera()
listenForLockscreenToAod()
listenForLockscreenToAodOrDozing()
listenForLockscreenToBouncer()
listenForLockscreenToDreaming()
listenForLockscreenToBouncerDragging()
@@ -230,19 +230,31 @@ constructor(
}
}
private fun listenForLockscreenToAod() {
private fun listenForLockscreenToAodOrDozing() {
scope.launch {
keyguardInteractor
.dozeTransitionTo(DozeStateModel.DOZE_AOD)
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (dozeToAod, lastStartedStep) = pair
if (lastStartedStep.to == KeyguardState.LOCKSCREEN) {
keyguardInteractor.wakefulnessModel
.sample(
combine(
keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isAodAvailable,
::Pair
),
::toTriple
)
.collect { (wakefulnessState, lastStartedStep, isAodAvailable) ->
if (
lastStartedStep.to == KeyguardState.LOCKSCREEN &&
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.LOCKSCREEN,
KeyguardState.AOD,
if (isAodAvailable) {
KeyguardState.AOD
} else {
KeyguardState.DOZING
},
getAnimator(),
)
)

View File

@@ -23,12 +23,14 @@ 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.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@SysUISingleton
@@ -44,6 +46,7 @@ constructor(
override fun start() {
listenForOccludedToLockscreen()
listenForOccludedToDreaming()
listenForOccludedToAodOrDozing()
}
private fun listenForOccludedToDreaming() {
@@ -70,8 +73,7 @@ constructor(
scope.launch {
keyguardInteractor.isKeyguardOccluded
.sample(keyguardTransitionInteractor.startedKeyguardTransitionStep, ::Pair)
.collect { pair ->
val (isOccluded, lastStartedKeyguardState) = pair
.collect { (isOccluded, lastStartedKeyguardState) ->
// Occlusion signals come from the framework, and should interrupt any
// existing transition
if (!isOccluded && lastStartedKeyguardState.to == KeyguardState.OCCLUDED) {
@@ -88,6 +90,39 @@ constructor(
}
}
private fun listenForOccludedToAodOrDozing() {
scope.launch {
keyguardInteractor.wakefulnessModel
.sample(
combine(
keyguardTransitionInteractor.startedKeyguardTransitionStep,
keyguardInteractor.isAodAvailable,
::Pair
),
::toTriple
)
.collect { (wakefulnessState, lastStartedStep, isAodAvailable) ->
if (
lastStartedStep.to == KeyguardState.OCCLUDED &&
wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
KeyguardState.OCCLUDED,
if (isAodAvailable) {
KeyguardState.AOD
} else {
KeyguardState.DOZING
},
getAnimator(),
)
)
}
}
}
}
private fun getAnimator(duration: Duration = DEFAULT_DURATION): ValueAnimator {
return ValueAnimator().apply {
setInterpolator(Interpolators.LINEAR)

View File

@@ -57,6 +57,8 @@ constructor(
val dozeAmount: Flow<Float> = repository.linearDozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
/** Whether Always-on Display mode is available. */
val isAodAvailable: Flow<Boolean> = repository.isAodAvailable
/** Doze transition information. */
val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
/**

View File

@@ -4267,8 +4267,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
&& !mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}

View File

@@ -44,10 +44,9 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.tuner.TunerService;
@@ -82,7 +81,6 @@ public class DozeParameters implements
private final AlwaysOnDisplayPolicy mAlwaysOnPolicy;
private final Resources mResources;
private final BatteryController mBatteryController;
private final FeatureFlags mFeatureFlags;
private final ScreenOffAnimationController mScreenOffAnimationController;
private final FoldAodAnimationController mFoldAodAnimationController;
private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
@@ -125,7 +123,6 @@ public class DozeParameters implements
BatteryController batteryController,
TunerService tunerService,
DumpManager dumpManager,
FeatureFlags featureFlags,
ScreenOffAnimationController screenOffAnimationController,
Optional<SysUIUnfoldComponent> sysUiUnfoldComponent,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@@ -141,7 +138,6 @@ public class DozeParameters implements
mControlScreenOffAnimation = !getDisplayNeedsBlanking();
mPowerManager = powerManager;
mPowerManager.setDozeAfterScreenOff(!mControlScreenOffAnimation);
mFeatureFlags = featureFlags;
mScreenOffAnimationController = screenOffAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
@@ -162,6 +158,13 @@ public class DozeParameters implements
SettingsObserver quickPickupSettingsObserver = new SettingsObserver(context, handler);
quickPickupSettingsObserver.observe();
batteryController.addCallback(new BatteryStateChangeCallback() {
@Override
public void onPowerSaveChanged(boolean isPowerSave) {
dispatchAlwaysOnEvent();
}
});
}
private void updateQuickPickupEnabled() {
@@ -300,13 +303,10 @@ public class DozeParameters implements
/**
* Whether we're capable of controlling the screen off animation if we want to. This isn't
* possible if AOD isn't even enabled or if the flag is disabled, or if the display needs
* blanking.
* possible if AOD isn't even enabled or if the display needs blanking.
*/
public boolean canControlUnlockedScreenOff() {
return getAlwaysOn()
&& mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
&& !getDisplayNeedsBlanking();
return getAlwaysOn() && !getDisplayNeedsBlanking();
}
/**
@@ -424,9 +424,7 @@ public class DozeParameters implements
updateControlScreenOff();
}
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
dispatchAlwaysOnEvent();
mScreenOffAnimationController.onAlwaysOnChanged(getAlwaysOn());
}
@@ -463,6 +461,12 @@ public class DozeParameters implements
pw.print("isQuickPickupEnabled(): "); pw.println(isQuickPickupEnabled());
}
private void dispatchAlwaysOnEvent() {
for (Callback callback : mCallbacks) {
callback.onAlwaysOnChange();
}
}
private boolean getPostureSpecificBool(
int[] postureMapping,
boolean defaultSensorBool,
@@ -477,7 +481,8 @@ public class DozeParameters implements
return bool;
}
interface Callback {
/** Callbacks for doze parameter related information */
public interface Callback {
/**
* Invoked when the value of getAlwaysOn may have changed.
*/

View File

@@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.shared.model.Position
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine
import com.android.systemui.doze.DozeTransitionCallback
@@ -38,14 +39,17 @@ import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -68,6 +72,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var authController: AuthController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var dreamOverlayCallbackController: DreamOverlayCallbackController
@Mock private lateinit var dozeParameters: DozeParameters
private lateinit var underTest: KeyguardRepositoryImpl
@@ -84,6 +89,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
dozeParameters,
authController,
dreamOverlayCallbackController,
)
@@ -169,6 +175,26 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
}
@Test
fun isAodAvailable() = runTest {
val flow = underTest.isAodAvailable
var isAodAvailable = collectLastValue(flow)
runCurrent()
val callback =
withArgCaptor<DozeParameters.Callback> { verify(dozeParameters).addCallback(capture()) }
whenever(dozeParameters.getAlwaysOn()).thenReturn(false)
callback.onAlwaysOnChange()
assertThat(isAodAvailable()).isEqualTo(false)
whenever(dozeParameters.getAlwaysOn()).thenReturn(true)
callback.onAlwaysOnChange()
assertThat(isAodAvailable()).isEqualTo(true)
flow.onCompletion { verify(dozeParameters).removeCallback(callback) }
}
@Test
fun isKeyguardOccluded() =
runTest(UnconfinedTestDispatcher()) {

View File

@@ -71,6 +71,10 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
private lateinit var fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor
private lateinit var fromDreamingTransitionInteractor: FromDreamingTransitionInteractor
private lateinit var fromDozingTransitionInteractor: FromDozingTransitionInteractor
private lateinit var fromOccludedTransitionInteractor: FromOccludedTransitionInteractor
private lateinit var fromGoneTransitionInteractor: FromGoneTransitionInteractor
private lateinit var fromAodTransitionInteractor: FromAodTransitionInteractor
@Before
fun setUp() {
@@ -102,6 +106,42 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromDreamingTransitionInteractor.start()
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromAodTransitionInteractor.start()
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromGoneTransitionInteractor.start()
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromDozingTransitionInteractor.start()
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
fromOccludedTransitionInteractor.start()
}
@Test
@@ -192,6 +232,289 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
coroutineContext.cancelChildren()
}
@Test
fun `OCCLUDED to DOZING`() =
testScope.runTest {
// GIVEN a device with AOD not available
keyguardRepository.setAodAvailable(false)
runCurrent()
// GIVEN a prior transition has run to OCCLUDED
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.OCCLUDED,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to sleep
keyguardRepository.setWakefulnessModel(startingToSleep())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
assertThat(info.to).isEqualTo(KeyguardState.DOZING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `OCCLUDED to AOD`() =
testScope.runTest {
// GIVEN a device with AOD available
keyguardRepository.setAodAvailable(true)
runCurrent()
// GIVEN a prior transition has run to OCCLUDED
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.OCCLUDED,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to sleep
keyguardRepository.setWakefulnessModel(startingToSleep())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromOccludedTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.OCCLUDED)
assertThat(info.to).isEqualTo(KeyguardState.AOD)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `LOCKSCREEN to DOZING`() =
testScope.runTest {
// GIVEN a device with AOD not available
keyguardRepository.setAodAvailable(false)
runCurrent()
// GIVEN a prior transition has run to LOCKSCREEN
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.GONE,
to = KeyguardState.LOCKSCREEN,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to sleep
keyguardRepository.setWakefulnessModel(startingToSleep())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.to).isEqualTo(KeyguardState.DOZING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `LOCKSCREEN to AOD`() =
testScope.runTest {
// GIVEN a device with AOD available
keyguardRepository.setAodAvailable(true)
runCurrent()
// GIVEN a prior transition has run to LOCKSCREEN
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.GONE,
to = KeyguardState.LOCKSCREEN,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to sleep
keyguardRepository.setWakefulnessModel(startingToSleep())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromLockscreenTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.to).isEqualTo(KeyguardState.AOD)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `DOZING to LOCKSCREEN`() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.DOZING,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to wake
keyguardRepository.setWakefulnessModel(startingToWake())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromDozingTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.DOZING)
assertThat(info.to).isEqualTo(KeyguardState.LOCKSCREEN)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `GONE to DOZING`() =
testScope.runTest {
// GIVEN a device with AOD not available
keyguardRepository.setAodAvailable(false)
runCurrent()
// GIVEN a prior transition has run to GONE
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to sleep
keyguardRepository.setWakefulnessModel(startingToSleep())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.GONE)
assertThat(info.to).isEqualTo(KeyguardState.DOZING)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
@Test
fun `GONE to AOD`() =
testScope.runTest {
// GIVEN a device with AOD available
keyguardRepository.setAodAvailable(true)
runCurrent()
// GIVEN a prior transition has run to GONE
runner.startTransition(
testScope,
TransitionInfo(
ownerName = "",
from = KeyguardState.LOCKSCREEN,
to = KeyguardState.GONE,
animator =
ValueAnimator().apply {
duration = 10
interpolator = Interpolators.LINEAR
},
)
)
runCurrent()
reset(mockTransitionRepository)
// WHEN the device begins to sleep
keyguardRepository.setWakefulnessModel(startingToSleep())
runCurrent()
val info =
withArgCaptor<TransitionInfo> {
verify(mockTransitionRepository).startTransition(capture())
}
// THEN a transition to DOZING should occur
assertThat(info.ownerName).isEqualTo("FromGoneTransitionInteractor")
assertThat(info.from).isEqualTo(KeyguardState.GONE)
assertThat(info.to).isEqualTo(KeyguardState.AOD)
assertThat(info.animator).isNotNull()
coroutineContext.cancelChildren()
}
private fun startingToWake() =
WakefulnessModel(
WakefulnessState.STARTING_TO_WAKE,
@@ -199,4 +522,12 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
WakeSleepReason.OTHER,
WakeSleepReason.OTHER
)
private fun startingToSleep() =
WakefulnessModel(
WakefulnessState.STARTING_TO_SLEEP,
true,
WakeSleepReason.OTHER,
WakeSleepReason.OTHER
)
}

View File

@@ -23,6 +23,10 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Resources;
@@ -39,10 +43,9 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.doze.AlwaysOnDisplayPolicy;
import com.android.systemui.doze.DozeScreenState;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.unfold.FoldAodAnimationController;
@@ -52,6 +55,8 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -69,7 +74,6 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private PowerManager mPowerManager;
@Mock private TunerService mTunerService;
@Mock private BatteryController mBatteryController;
@Mock private FeatureFlags mFeatureFlags;
@Mock private DumpManager mDumpManager;
@Mock private ScreenOffAnimationController mScreenOffAnimationController;
@Mock private FoldAodAnimationController mFoldAodAnimationController;
@@ -78,6 +82,7 @@ public class DozeParametersTest extends SysuiTestCase {
@Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock private StatusBarStateController mStatusBarStateController;
@Mock private ConfigurationController mConfigurationController;
@Captor private ArgumentCaptor<BatteryStateChangeCallback> mBatteryStateChangeCallback;
/**
* The current value of PowerManager's dozeAfterScreenOff property.
@@ -113,7 +118,6 @@ public class DozeParametersTest extends SysuiTestCase {
mBatteryController,
mTunerService,
mDumpManager,
mFeatureFlags,
mScreenOffAnimationController,
Optional.of(mSysUIUnfoldComponent),
mUnlockedScreenOffAnimationController,
@@ -122,7 +126,8 @@ public class DozeParametersTest extends SysuiTestCase {
mStatusBarStateController
);
when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(true);
verify(mBatteryController).addCallback(mBatteryStateChangeCallback.capture());
setAodEnabledForTest(true);
setShouldControlUnlockedScreenOffForTest(true);
setDisplayNeedsBlankingForTest(false);
@@ -173,6 +178,29 @@ public class DozeParametersTest extends SysuiTestCase {
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
}
@Test
public void testGetAlwaysOn_whenBatterySaverCallback() {
DozeParameters.Callback callback = mock(DozeParameters.Callback.class);
mDozeParameters.addCallback(callback);
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
when(mBatteryController.isAodPowerSave()).thenReturn(true);
// Both lines should trigger an event
mDozeParameters.onTuningChanged(Settings.Secure.DOZE_ALWAYS_ON, "1");
mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
verify(callback, times(2)).onAlwaysOnChange();
assertThat(mDozeParameters.getAlwaysOn()).isFalse();
reset(callback);
when(mBatteryController.isAodPowerSave()).thenReturn(false);
mBatteryStateChangeCallback.getValue().onPowerSaveChanged(true);
verify(callback).onAlwaysOnChange();
assertThat(mDozeParameters.getAlwaysOn()).isTrue();
}
/**
* PowerManager.setDozeAfterScreenOff(true) means we are not controlling screen off, and calling
* it with false means we are. Confusing, but sure - make sure that we call PowerManager with
@@ -195,17 +223,6 @@ public class DozeParametersTest extends SysuiTestCase {
assertFalse(mPowerManagerDozeAfterScreenOff);
}
@Test
public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() {
when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(false);
assertFalse(mDozeParameters.shouldControlUnlockedScreenOff());
// Trigger the setter for the current value.
mDozeParameters.setControlScreenOffAnimation(mDozeParameters.shouldControlScreenOff());
assertFalse(mDozeParameters.shouldControlScreenOff());
}
@Test
public void propagatesAnimateScreenOff_noAlwaysOn() {
setAodEnabledForTest(false);

View File

@@ -52,6 +52,9 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _isDozing = MutableStateFlow(false)
override val isDozing: Flow<Boolean> = _isDozing
private val _isAodAvailable = MutableStateFlow(false)
override val isAodAvailable: Flow<Boolean> = _isAodAvailable
private val _isDreaming = MutableStateFlow(false)
override val isDreaming: Flow<Boolean> = _isDreaming
@@ -126,6 +129,10 @@ class FakeKeyguardRepository : KeyguardRepository {
_isDozing.value = isDozing
}
fun setAodAvailable(isAodAvailable: Boolean) {
_isAodAvailable.value = isAodAvailable
}
fun setDreamingWithOverlay(isDreaming: Boolean) {
_isDreamingWithOverlay.value = isDreaming
}