Merge "Add WakeDirectlyToGoneInteractor." into main
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index c4b70d8..9f33113 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -81,6 +81,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardWakeDirectlyToGoneInteractor;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindParamsApplier;
import com.android.systemui.keyguard.ui.binder.KeyguardSurfaceBehindViewBinder;
import com.android.systemui.keyguard.ui.binder.WindowManagerLockscreenVisibilityViewBinder;
@@ -317,7 +318,7 @@
private final WindowManagerOcclusionManager mWmOcclusionManager;
private final KeyguardEnabledInteractor mKeyguardEnabledInteractor;
-
+ private final KeyguardWakeDirectlyToGoneInteractor mKeyguardWakeDirectlyToGoneInteractor;
private final Lazy<FoldGracePeriodProvider> mFoldGracePeriodProvider = new Lazy<>() {
@Override
public FoldGracePeriodProvider get() {
@@ -344,7 +345,8 @@
@Main Executor mainExecutor,
KeyguardInteractor keyguardInteractor,
KeyguardEnabledInteractor keyguardEnabledInteractor,
- Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy) {
+ Lazy<KeyguardStateCallbackStartable> keyguardStateCallbackStartableLazy,
+ KeyguardWakeDirectlyToGoneInteractor keyguardWakeDirectlyToGoneInteractor) {
super();
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
@@ -372,6 +374,7 @@
mWmOcclusionManager = windowManagerOcclusionManager;
mKeyguardEnabledInteractor = keyguardEnabledInteractor;
+ mKeyguardWakeDirectlyToGoneInteractor = keyguardWakeDirectlyToGoneInteractor;
}
@Override
@@ -486,6 +489,7 @@
public void onDreamingStarted() {
trace("onDreamingStarted");
checkPermission();
+ mKeyguardWakeDirectlyToGoneInteractor.onDreamingStarted();
mKeyguardInteractor.setDreaming(true);
mKeyguardViewMediator.onDreamingStarted();
}
@@ -494,6 +498,7 @@
public void onDreamingStopped() {
trace("onDreamingStopped");
checkPermission();
+ mKeyguardWakeDirectlyToGoneInteractor.onDreamingStopped();
mKeyguardInteractor.setDreaming(false);
mKeyguardViewMediator.onDreamingStopped();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index f837d8e..ae751db 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -127,6 +127,30 @@
*/
val isKeyguardEnabled: StateFlow<Boolean>
+ /**
+ * Whether we can transition directly back to GONE from AOD/DOZING without any authentication
+ * events (such as a fingerprint wake and unlock), even though authentication would normally be
+ * required. This means that if you tap the screen or press the power button, you'll return
+ * directly to the unlocked app content without seeing the lockscreen, even if a secure
+ * authentication method (PIN/password/biometrics) is set.
+ *
+ * This is true in these cases:
+ * - The screen timed out, but the "lock after screen timeout" duration (default 5 seconds) has
+ * not yet elapsed.
+ * - The power button was pressed, but "power button instantly locks" is not enabled, and the
+ * "lock after screen timeout" duration has not elapsed.
+ *
+ * Note that this value specifically tells us if we can *ignore* authentication that would
+ * otherwise be required to transition from AOD/DOZING -> GONE. AOD/DOZING -> GONE is also
+ * possible if keyguard is disabled, either from an app request or because security is set to
+ * "none", but in that case, auth is not required so this boolean is not relevant.
+ *
+ * See [KeyguardWakeToGoneInteractor].
+ */
+ val canIgnoreAuthAndReturnToGone: StateFlow<Boolean>
+
+ fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean)
+
/** Is the always-on display available to be used? */
val isAodAvailable: StateFlow<Boolean>
@@ -386,6 +410,13 @@
MutableStateFlow(!lockPatternUtils.isLockScreenDisabled(userTracker.userId))
override val isKeyguardEnabled: StateFlow<Boolean> = _isKeyguardEnabled.asStateFlow()
+ private val _canIgnoreAuthAndReturnToGone = MutableStateFlow(false)
+ override val canIgnoreAuthAndReturnToGone = _canIgnoreAuthAndReturnToGone.asStateFlow()
+
+ override fun setCanIgnoreAuthAndReturnToGone(canWakeToGone: Boolean) {
+ _canIgnoreAuthAndReturnToGone.value = canWakeToGone
+ }
+
private val _isDozing = MutableStateFlow(statusBarStateController.isDozing)
override val isDozing: StateFlow<Boolean> = _isDozing.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 868c462..1167cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -53,6 +53,7 @@
powerInteractor: PowerInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
val deviceEntryRepository: DeviceEntryRepository,
+ private val wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.AOD,
@@ -98,6 +99,7 @@
keyguardInteractor.primaryBouncerShowing,
keyguardInteractor.isKeyguardOccluded,
canDismissLockscreen,
+ wakeToGoneInteractor.canWakeDirectlyToGone,
)
.collect {
(
@@ -107,6 +109,7 @@
primaryBouncerShowing,
isKeyguardOccludedLegacy,
canDismissLockscreen,
+ canWakeDirectlyToGone,
) ->
if (!maybeHandleInsecurePowerGesture()) {
val shouldTransitionToLockscreen =
@@ -131,8 +134,7 @@
val shouldTransitionToGone =
(!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) ||
- (KeyguardWmStateRefactor.isEnabled &&
- !deviceEntryRepository.isLockscreenEnabled())
+ (KeyguardWmStateRefactor.isEnabled && canWakeDirectlyToGone)
if (shouldTransitionToGone) {
startTransitionTo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
index 76e88a2..aee65a8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractor.kt
@@ -55,6 +55,7 @@
private val communalInteractor: CommunalInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
val deviceEntryRepository: DeviceEntryRepository,
+ private val wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.DOZING,
@@ -181,7 +182,7 @@
.sample(
communalInteractor.isIdleOnCommunal,
keyguardInteractor.biometricUnlockState,
- canTransitionToGoneOnWake,
+ wakeToGoneInteractor.canWakeDirectlyToGone,
keyguardInteractor.primaryBouncerShowing,
)
.collect {
@@ -189,27 +190,14 @@
_,
isIdleOnCommunal,
biometricUnlockState,
- canDismissLockscreen,
+ canWakeDirectlyToGone,
primaryBouncerShowing) ->
if (
!maybeStartTransitionToOccludedOrInsecureCamera() &&
// Handled by dismissFromDozing().
!isWakeAndUnlock(biometricUnlockState.mode)
) {
- if (!KeyguardWmStateRefactor.isEnabled && canDismissLockscreen) {
- if (SceneContainerFlag.isEnabled) {
- // TODO(b/336576536): Check if adaptation for scene framework is
- // needed
- } else {
- startTransitionTo(
- KeyguardState.GONE,
- ownerReason = "waking from dozing"
- )
- }
- } else if (
- KeyguardWmStateRefactor.isEnabled &&
- !deviceEntryRepository.isLockscreenEnabled()
- ) {
+ if (canWakeDirectlyToGone) {
if (SceneContainerFlag.isEnabled) {
// TODO(b/336576536): Check if adaptation for scene framework is
// needed
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index 0e76487..cfb161c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -23,6 +23,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
@@ -37,11 +38,14 @@
import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
+import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class FromDreamingTransitionInteractor
@Inject
@@ -56,6 +60,7 @@
private val glanceableHubTransitions: GlanceableHubTransitions,
powerInteractor: PowerInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+ private val deviceEntryInteractor: DeviceEntryInteractor,
) :
TransitionInteractor(
fromState = KeyguardState.DREAMING,
@@ -72,7 +77,7 @@
listenForDreamingToOccluded()
listenForDreamingToGoneWhenDismissable()
listenForDreamingToGoneFromBiometricUnlock()
- listenForDreamingToLockscreen()
+ listenForDreamingToLockscreenOrGone()
listenForDreamingToAodOrDozing()
listenForTransitionToCamera(scope, keyguardInteractor)
listenForDreamingToGlanceableHub()
@@ -132,17 +137,7 @@
@OptIn(FlowPreview::class)
private fun listenForDreamingToOccluded() {
- if (KeyguardWmStateRefactor.isEnabled) {
- scope.launch {
- combine(
- keyguardInteractor.isDreaming,
- keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop,
- ::Pair
- )
- .filterRelevantKeyguardStateAnd { (isDreaming, _) -> !isDreaming }
- .collect { maybeStartTransitionToOccludedOrInsecureCamera() }
- }
- } else {
+ if (!KeyguardWmStateRefactor.isEnabled) {
scope.launch {
combine(
keyguardInteractor.isKeyguardOccluded,
@@ -168,21 +163,41 @@
}
}
- private fun listenForDreamingToLockscreen() {
+ private fun listenForDreamingToLockscreenOrGone() {
if (!KeyguardWmStateRefactor.isEnabled) {
return
}
scope.launch {
- keyguardOcclusionInteractor.isShowWhenLockedActivityOnTop
- .filterRelevantKeyguardStateAnd { onTop -> !onTop }
- .collect { startTransitionTo(KeyguardState.LOCKSCREEN) }
+ keyguardInteractor.isDreaming
+ .filter { !it }
+ .sample(deviceEntryInteractor.isUnlocked, ::Pair)
+ .collect { (_, dismissable) ->
+ // TODO(b/349837588): Add check for -> OCCLUDED.
+ if (dismissable) {
+ startTransitionTo(
+ KeyguardState.GONE,
+ ownerReason = "No longer dreaming; dismissable"
+ )
+ } else {
+ startTransitionTo(
+ KeyguardState.LOCKSCREEN,
+ ownerReason = "No longer dreaming"
+ )
+ }
+ }
}
}
private fun listenForDreamingToGoneWhenDismissable() {
- // TODO(b/336576536): Check if adaptation for scene framework is needed
- if (SceneContainerFlag.isEnabled) return
+ if (SceneContainerFlag.isEnabled) {
+ return // TODO(b/336576536): Check if adaptation for scene framework is needed
+ }
+
+ if (KeyguardWmStateRefactor.isEnabled) {
+ return
+ }
+
scope.launch {
keyguardInteractor.isAbleToDream
.sampleCombine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
new file mode 100644
index 0000000..6a1b7cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractor.kt
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2024 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.annotation.SuppressLint
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.provider.Settings
+import android.provider.Settings.Secure
+import com.android.internal.widget.LockPatternUtils
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.KeyguardViewMediator
+import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAsleepInState
+import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAwakeInState
+import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.util.kotlin.sample
+import com.android.systemui.util.settings.SecureSettings
+import com.android.systemui.util.settings.SystemSettings
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlin.math.max
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.distinctUntilChangedBy
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/**
+ * Logic related to the ability to wake directly to GONE from asleep (AOD/DOZING), without going
+ * through LOCKSCREEN or a BOUNCER state.
+ *
+ * This is possible in the following scenarios:
+ * - The lockscreen is disabled, either from an app request (SUW does this), or by the security
+ * "None" setting.
+ * - A biometric authentication event occurred while we were asleep (fingerprint auth, etc). This
+ * specifically is referred to throughout the codebase as "wake and unlock".
+ * - The screen timed out, but the "lock after screen timeout" duration has not elapsed.
+ * - The power button was pressed, but "power button instantly locks" is disabled and the "lock
+ * after screen timeout" duration has not elapsed.
+ *
+ * In these cases, no (further) authentication is required, and we can transition directly from
+ * AOD/DOZING -> GONE.
+ */
+@SysUISingleton
+class KeyguardWakeDirectlyToGoneInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val context: Context,
+ private val repository: KeyguardRepository,
+ private val systemClock: SystemClock,
+ private val alarmManager: AlarmManager,
+ private val transitionInteractor: KeyguardTransitionInteractor,
+ private val powerInteractor: PowerInteractor,
+ private val secureSettings: SecureSettings,
+ private val lockPatternUtils: LockPatternUtils,
+ private val systemSettings: SystemSettings,
+ private val selectedUserInteractor: SelectedUserInteractor,
+) {
+
+ /**
+ * Whether the lockscreen was disabled as of the last wake/sleep event, according to
+ * LockPatternUtils.
+ *
+ * This will always be true if [repository.isKeyguardServiceEnabled]=false, but it can also be
+ * true when the keyguard service is enabled if the lockscreen has been disabled via adb using
+ * the `adb shell locksettings set-disabled true` command, which is often done in tests.
+ *
+ * Unlike keyguardServiceEnabled, changes to this value should *not* immediately show or hide
+ * the keyguard. If the lockscreen is disabled in this way, it will just not show on the next
+ * sleep/wake.
+ */
+ private val isLockscreenDisabled: Flow<Boolean> =
+ powerInteractor.isAwake.map { isLockscreenDisabled() }
+
+ /**
+ * Whether we can wake from AOD/DOZING directly to GONE, bypassing LOCKSCREEN/BOUNCER states.
+ *
+ * This is possible in the following cases:
+ * - Keyguard is disabled, either from an app request or from security being set to "None".
+ * - We're wake and unlocking (fingerprint auth occurred while asleep).
+ * - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing.
+ */
+ val canWakeDirectlyToGone =
+ combine(
+ repository.isKeyguardEnabled,
+ isLockscreenDisabled,
+ repository.biometricUnlockState,
+ repository.canIgnoreAuthAndReturnToGone,
+ ) {
+ keyguardEnabled,
+ isLockscreenDisabled,
+ biometricUnlockState,
+ canIgnoreAuthAndReturnToGone ->
+ (!keyguardEnabled || isLockscreenDisabled) ||
+ BiometricUnlockMode.isWakeAndUnlock(biometricUnlockState.mode) ||
+ canIgnoreAuthAndReturnToGone
+ }
+ .distinctUntilChanged()
+
+ /**
+ * Counter that is incremented every time we wake up or stop dreaming. Upon sleeping/dreaming,
+ * we put the current value of this counter into the intent extras of the timeout alarm intent.
+ * If this value has changed by the time we receive the intent, it is discarded since it's out
+ * of date.
+ */
+ var timeoutCounter = 0
+
+ var isAwake = false
+
+ private val broadcastReceiver: BroadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (DELAYED_KEYGUARD_ACTION == intent.action) {
+ val sequence = intent.getIntExtra(SEQ_EXTRA_KEY, 0)
+ synchronized(this) {
+ if (timeoutCounter == sequence) {
+ // If the sequence # matches, we have not woken up or stopped dreaming
+ // since
+ // the alarm was set. That means this is still relevant - the lock
+ // timeout
+ // has elapsed, so let the repository know that we can no longer return
+ // to
+ // GONE without authenticating.
+ repository.setCanIgnoreAuthAndReturnToGone(false)
+ }
+ }
+ }
+ }
+ }
+
+ init {
+ setOrCancelAlarmFromWakefulness()
+ listenForWakeToClearCanIgnoreAuth()
+ registerBroadcastReceiver()
+ }
+
+ fun onDreamingStarted() {
+ // If we start dreaming while awake, lock after the normal timeout.
+ if (isAwake) {
+ setResetCanIgnoreAuthAlarm()
+ }
+ }
+
+ fun onDreamingStopped() {
+ // Cancel the timeout if we stop dreaming while awake.
+ if (isAwake) {
+ cancelCanIgnoreAuthAlarm()
+ }
+ }
+
+ private fun setOrCancelAlarmFromWakefulness() {
+ scope.launch {
+ powerInteractor.detailedWakefulness
+ .distinctUntilChangedBy { it.isAwake() }
+ .sample(transitionInteractor.currentKeyguardState, ::Pair)
+ .collect { (wakefulness, currentState) ->
+ // Save isAwake for use in onDreamingStarted/onDreamingStopped.
+ this@KeyguardWakeDirectlyToGoneInteractor.isAwake = wakefulness.isAwake()
+
+ // If we're sleeping from GONE, check the timeout and lock instantly settings.
+ // These are not relevant if we're coming from non-GONE states.
+ if (!isAwake && currentState == KeyguardState.GONE) {
+ val lockTimeoutDuration = getCanIgnoreAuthAndReturnToGoneDuration()
+
+ // If the screen timed out and went to sleep, and the lock timeout is > 0ms,
+ // then we can return to GONE until that duration elapses. If the power
+ // button was pressed but "instantly locks" is disabled, then we can also
+ // return to GONE until the timeout duration elapses.
+ if (
+ (wakefulness.lastSleepReason == WakeSleepReason.TIMEOUT &&
+ lockTimeoutDuration > 0) ||
+ (wakefulness.lastSleepReason == WakeSleepReason.POWER_BUTTON &&
+ !willLockImmediately())
+ ) {
+
+ // Let the repository know that we can return to GONE until we notify
+ // it otherwise.
+ repository.setCanIgnoreAuthAndReturnToGone(true)
+ setResetCanIgnoreAuthAlarm()
+ }
+ } else if (isAwake) {
+ // If we're waking up, ignore the alarm if it goes off since it's no longer
+ // relevant. Once a wake KeyguardTransition is started, we'll also clear the
+ // canIgnoreAuthAndReturnToGone value in listenForWakeToClearCanIgnoreAuth.
+ cancelCanIgnoreAuthAlarm()
+ }
+ }
+ }
+ }
+
+ /** Clears the canIgnoreAuthAndReturnToGone value upon waking. */
+ private fun listenForWakeToClearCanIgnoreAuth() {
+ scope.launch {
+ transitionInteractor
+ .isInTransitionWhere(
+ fromStatePredicate = { deviceIsAsleepInState(it) },
+ toStatePredicate = { deviceIsAwakeInState(it) },
+ )
+ .collect {
+ // This value is reset when the timeout alarm fires, but if the device is woken
+ // back up before then, it needs to be reset here. The alarm is cancelled
+ // immediately upon waking up, but since this value is used by keyguard
+ // transition internals to decide whether we can transition to GONE, wait until
+ // that decision is made before resetting it.
+ repository.setCanIgnoreAuthAndReturnToGone(false)
+ }
+ }
+ }
+
+ /**
+ * Registers the broadcast receiver to receive the alarm intent.
+ *
+ * TODO(b/351817381): Investigate using BroadcastDispatcher vs. ignoring this lint warning.
+ */
+ @SuppressLint("WrongConstant", "RegisterReceiverViaContext")
+ private fun registerBroadcastReceiver() {
+ val delayedActionFilter = IntentFilter()
+ delayedActionFilter.addAction(KeyguardViewMediator.DELAYED_KEYGUARD_ACTION)
+ // TODO(b/346803756): Listen for DELAYED_LOCK_PROFILE_ACTION.
+ delayedActionFilter.priority = IntentFilter.SYSTEM_HIGH_PRIORITY
+ context.registerReceiver(
+ broadcastReceiver,
+ delayedActionFilter,
+ SYSTEMUI_PERMISSION,
+ null /* scheduler */,
+ Context.RECEIVER_EXPORTED_UNAUDITED
+ )
+ }
+
+ /** Set an alarm for */
+ private fun setResetCanIgnoreAuthAlarm() {
+ val intent =
+ Intent(DELAYED_KEYGUARD_ACTION).apply {
+ setPackage(context.packageName)
+ putExtra(SEQ_EXTRA_KEY, timeoutCounter)
+ addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ }
+
+ val sender =
+ PendingIntent.getBroadcast(
+ context,
+ 0,
+ intent,
+ PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
+ val time = systemClock.elapsedRealtime() + getCanIgnoreAuthAndReturnToGoneDuration()
+ alarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, time, sender)
+
+ // TODO(b/346803756): Migrate support for child profiles.
+ }
+
+ /**
+ * Cancel the timeout by incrementing the counter so that we ignore the intent when it's
+ * received.
+ */
+ private fun cancelCanIgnoreAuthAlarm() {
+ timeoutCounter++
+ }
+
+ /**
+ * Whether pressing the power button locks the device immediately; vs. waiting for a specified
+ * timeout first.
+ */
+ private fun willLockImmediately(
+ userId: Int = selectedUserInteractor.getSelectedUserId()
+ ): Boolean {
+ return lockPatternUtils.getPowerButtonInstantlyLocks(userId) ||
+ !lockPatternUtils.isSecure(userId)
+ }
+
+ /**
+ * Returns whether the lockscreen is disabled, either because the keyguard service is disabled
+ * or because an adb command has disabled the lockscreen.
+ */
+ private fun isLockscreenDisabled(
+ userId: Int = selectedUserInteractor.getSelectedUserId()
+ ): Boolean {
+ return lockPatternUtils.isLockScreenDisabled(userId)
+ }
+
+ /**
+ * Returns the duration within which we can return to GONE without auth after a screen timeout
+ * (or power button press, if lock instantly is disabled).
+ *
+ * This takes into account the user's settings as well as device policy maximums.
+ */
+ private fun getCanIgnoreAuthAndReturnToGoneDuration(
+ userId: Int = selectedUserInteractor.getSelectedUserId()
+ ): Long {
+ // The timeout duration from settings (Security > Device Unlock > Gear icon > "Lock after
+ // screen timeout".
+ val durationSetting: Long =
+ secureSettings
+ .getIntForUser(
+ Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KEYGUARD_CAN_IGNORE_AUTH_DURATION,
+ userId
+ )
+ .toLong()
+
+ // Device policy maximum timeout.
+ val durationDevicePolicyMax =
+ lockPatternUtils.devicePolicyManager.getMaximumTimeToLock(null, userId)
+
+ return if (durationDevicePolicyMax <= 0) {
+ durationSetting
+ } else {
+ var displayTimeout =
+ systemSettings
+ .getIntForUser(
+ Settings.System.SCREEN_OFF_TIMEOUT,
+ KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT,
+ userId
+ )
+ .toLong()
+
+ // Ignore negative values. I don't know why this would be negative, but this check has
+ // been around since 2016 and I see no upside to removing it.
+ displayTimeout = max(displayTimeout, 0)
+
+ // Respect the shorter of: the device policy (maximum duration between last user action
+ // and fully locking) or the "Lock after screen timeout" setting.
+ max(min(durationDevicePolicyMax - displayTimeout, durationSetting), 0)
+ }
+ }
+
+ companion object {
+ private const val DELAYED_KEYGUARD_ACTION =
+ "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD"
+ private const val DELAYED_LOCK_PROFILE_ACTION =
+ "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_LOCK"
+ private const val SYSTEMUI_PERMISSION = "com.android.systemui.permission.SELF"
+ private const val SEQ_EXTRA_KEY = "count"
+
+ private const val KEYGUARD_CAN_IGNORE_AUTH_DURATION = 5000
+ private const val KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 3355ffd..0985e69 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -21,14 +21,17 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.Edge
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.Companion.deviceIsAsleepInState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Lazy
@@ -41,11 +44,13 @@
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
+@OptIn(ExperimentalCoroutinesApi::class)
@SysUISingleton
class WindowManagerLockscreenVisibilityInteractor
@Inject
constructor(
keyguardInteractor: KeyguardInteractor,
+ transitionRepository: KeyguardTransitionRepository,
transitionInteractor: KeyguardTransitionInteractor,
surfaceBehindInteractor: KeyguardSurfaceBehindInteractor,
fromLockscreenInteractor: FromLockscreenTransitionInteractor,
@@ -54,9 +59,15 @@
notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
sceneInteractor: Lazy<SceneInteractor>,
deviceEntryInteractor: Lazy<DeviceEntryInteractor>,
+ wakeToGoneInteractor: KeyguardWakeDirectlyToGoneInteractor,
) {
private val defaultSurfaceBehindVisibility =
- transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
+ combine(
+ transitionInteractor.finishedKeyguardState,
+ wakeToGoneInteractor.canWakeDirectlyToGone,
+ ) { finishedState, canWakeDirectlyToGone ->
+ isSurfaceVisible(finishedState) || canWakeDirectlyToGone
+ }
/**
* Surface visibility provided by the From*TransitionInteractor responsible for the currently
@@ -203,9 +214,13 @@
if (SceneContainerFlag.isEnabled) {
isDeviceNotEntered
} else {
- transitionInteractor.currentKeyguardState
- .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
- .map { (currentState, startedWithPrev) ->
+ combine(
+ transitionInteractor.currentKeyguardState,
+ wakeToGoneInteractor.canWakeDirectlyToGone,
+ ::Pair
+ )
+ .sample(transitionInteractor.startedStepWithPrecedingStep, ::toTriple)
+ .map { (currentState, canWakeDirectlyToGone, startedWithPrev) ->
val startedFromStep = startedWithPrev.previousValue
val startedStep = startedWithPrev.newValue
val returningToGoneAfterCancellation =
@@ -213,16 +228,33 @@
startedFromStep.transitionState == TransitionState.CANCELED &&
startedFromStep.from == KeyguardState.GONE
- if (!returningToGoneAfterCancellation) {
- // By default, apply the lockscreen visibility of the current state.
- deviceEntryInteractor.get().isLockscreenEnabled() &&
- KeyguardState.lockscreenVisibleInState(currentState)
- } else {
- // If we're transitioning to GONE after a prior canceled transition from
- // GONE, then this is the camera launch transition from an asleep state back
- // to GONE. We don't want to show the lockscreen since we're aborting the
- // lock and going back to GONE.
+ val transitionInfo = transitionRepository.currentTransitionInfoInternal.value
+ val wakingDirectlyToGone =
+ deviceIsAsleepInState(transitionInfo.from) &&
+ transitionInfo.to == KeyguardState.GONE
+
+ if (returningToGoneAfterCancellation || wakingDirectlyToGone) {
+ // GONE -> AOD/DOZING (cancel) -> GONE is the camera launch transition,
+ // which means we never want to show the lockscreen throughout the
+ // transition. Same for waking directly to gone, due to the lockscreen being
+ // disabled or because the device was woken back up before the lock timeout
+ // duration elapsed.
KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+ } else if (canWakeDirectlyToGone) {
+ // Never show the lockscreen if we can wake directly to GONE. This means
+ // that the lock timeout has not yet elapsed, or the keyguard is disabled.
+ // In either case, we don't show the activity lock screen until one of those
+ // conditions changes.
+ false
+ } else if (
+ currentState == KeyguardState.DREAMING &&
+ deviceEntryInteractor.get().isUnlocked.value
+ ) {
+ // Dreams dismiss keyguard and return to GONE if they can.
+ false
+ } else {
+ // Otherwise, use the visibility of the current state.
+ KeyguardState.lockscreenVisibleInState(currentState)
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
index 75055668..776a8f4 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakeSleepReason.kt
@@ -54,7 +54,10 @@
OTHER(isTouch = false, PowerManager.WAKE_REASON_UNKNOWN),
/** Device goes to sleep due to folding of a foldable device. */
- FOLD(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD);
+ FOLD(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD),
+
+ /** Device goes to sleep because it timed out. */
+ TIMEOUT(isTouch = false, PowerManager.GO_TO_SLEEP_REASON_TIMEOUT);
companion object {
fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
@@ -75,6 +78,7 @@
fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
return when (reason) {
PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+ PowerManager.GO_TO_SLEEP_REASON_TIMEOUT -> TIMEOUT
PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD -> FOLD
else -> OTHER
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
index 42ab25f..032794c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorTest.kt
@@ -56,6 +56,7 @@
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
+import org.junit.Ignore
import org.junit.runner.RunWith
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
@@ -97,6 +98,7 @@
@Test
@EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+ @Ignore("Until b/349837588 is fixed")
fun testTransitionToOccluded_ifDreamEnds_occludingActivityOnTop() =
testScope.runTest {
kosmos.fakeKeyguardRepository.setDreaming(true)
@@ -156,6 +158,7 @@
reset(transitionRepository)
kosmos.keyguardOcclusionRepository.setShowWhenLockedActivityInfo(onTop = false)
+ kosmos.fakeKeyguardRepository.setDreaming(false)
runCurrent()
assertThat(transitionRepository)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
index 459e41d..ea5a41f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/InWindowLauncherUnlockAnimationInteractorTest.kt
@@ -18,25 +18,22 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.data.repository.FakeKeyguardSurfaceBehindRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.data.repository.InWindowLauncherUnlockAnimationRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.inWindowLauncherUnlockAnimationRepository
+import com.android.systemui.keyguard.data.repository.keyguardSurfaceBehindRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.util.mockTopActivityClassName
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.system.ActivityManagerWrapper
-import com.android.systemui.user.domain.UserDomainLayerModule
-import dagger.BindsInstance
-import dagger.Component
+import com.android.systemui.shared.system.activityManagerWrapper
+import com.android.systemui.testKosmos
import junit.framework.Assert.assertEquals
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -49,10 +46,16 @@
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class InWindowLauncherUnlockAnimationInteractorTest : SysuiTestCase() {
- private lateinit var underTest: InWindowLauncherUnlockAnimationInteractor
-
- private lateinit var testComponent: TestComponent
- private lateinit var testScope: TestScope
+ private val kosmos = testKosmos()
+ private val underTest =
+ InWindowLauncherUnlockAnimationInteractor(
+ kosmos.inWindowLauncherUnlockAnimationRepository,
+ kosmos.applicationCoroutineScope,
+ kosmos.keyguardTransitionInteractor,
+ { kosmos.keyguardSurfaceBehindRepository },
+ kosmos.activityManagerWrapper,
+ )
+ private val testScope = kosmos.testScope
private lateinit var transitionRepository: FakeKeyguardTransitionRepository
@Mock private lateinit var activityManagerWrapper: ActivityManagerWrapper
@@ -62,19 +65,9 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- testComponent =
- DaggerInWindowLauncherUnlockAnimationInteractorTest_TestComponent.factory()
- .create(
- test = this,
- mocks =
- TestMocksModule(
- activityManagerWrapper = activityManagerWrapper,
- ),
- )
- underTest = testComponent.underTest
- testScope = testComponent.testScope
- transitionRepository = testComponent.transitionRepository
+ transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ activityManagerWrapper = kosmos.activityManagerWrapper
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
}
@@ -92,7 +85,7 @@
)
// Put launcher on top
- testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
launcherClassName
)
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
@@ -175,7 +168,7 @@
)
// Put not launcher on top
- testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
launcherClassName
)
activityManagerWrapper.mockTopActivityClassName("not_launcher")
@@ -252,7 +245,7 @@
)
// Put launcher on top
- testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
launcherClassName
)
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
@@ -296,7 +289,7 @@
)
// Put Launcher on top and begin transitioning to GONE.
- testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
launcherClassName
)
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
@@ -316,7 +309,7 @@
values
)
- testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
+ kosmos.keyguardSurfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
runCurrent()
assertEquals(
@@ -360,7 +353,7 @@
)
// Put Launcher on top and begin transitioning to GONE.
- testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
launcherClassName
)
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
@@ -402,7 +395,7 @@
)
// Put Launcher on top and begin transitioning to GONE.
- testComponent.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
+ kosmos.inWindowLauncherUnlockAnimationRepository.setLauncherActivityClass(
launcherClassName
)
activityManagerWrapper.mockTopActivityClassName(launcherClassName)
@@ -427,7 +420,7 @@
to = KeyguardState.AOD,
)
)
- testComponent.surfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
+ kosmos.keyguardSurfaceBehindRepository.setSurfaceRemoteAnimationTargetAvailable(true)
runCurrent()
assertEquals(
@@ -437,29 +430,4 @@
values
)
}
-
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- BiometricsDomainLayerModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent {
- val underTest: InWindowLauncherUnlockAnimationInteractor
- val testScope: TestScope
- val transitionRepository: FakeKeyguardTransitionRepository
- val surfaceBehindRepository: FakeKeyguardSurfaceBehindRepository
- val inWindowLauncherUnlockAnimationRepository: InWindowLauncherUnlockAnimationRepository
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- mocks: TestMocksModule,
- ): TestComponent
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
new file mode 100644
index 0000000..22181f8
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2024 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.app.AlarmManager
+import android.app.admin.alarmManager
+import android.app.admin.devicePolicyManager
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.mockedContext
+import android.os.PowerManager
+import android.os.UserHandle
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.lockPatternUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.testKosmos
+import com.android.systemui.util.settings.fakeSettings
+import junit.framework.Assert.assertEquals
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyLong
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class KeyguardWakeDirectlyToGoneInteractorTest : SysuiTestCase() {
+
+ private var lastRegisteredBroadcastReceiver: BroadcastReceiver? = null
+ private val kosmos =
+ testKosmos().apply {
+ whenever(mockedContext.user).thenReturn(mock<UserHandle>())
+ doAnswer { invocation ->
+ lastRegisteredBroadcastReceiver = invocation.arguments[0] as BroadcastReceiver
+ }
+ .whenever(mockedContext)
+ .registerReceiver(any(), any(), any(), any(), any())
+ }
+
+ private val testScope = kosmos.testScope
+ private val underTest = kosmos.keyguardWakeDirectlyToGoneInteractor
+ private val lockPatternUtils = kosmos.lockPatternUtils
+ private val repository = kosmos.fakeKeyguardRepository
+ private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ @Test
+ fun testCanWakeDirectlyToGone_keyguardServiceEnabledThenDisabled() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false, // Defaults to false.
+ ),
+ canWake
+ )
+
+ repository.setKeyguardEnabled(false)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false, // Default to false.
+ true, // True now that keyguard service is disabled
+ ),
+ canWake
+ )
+
+ repository.setKeyguardEnabled(true)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ ),
+ canWake
+ )
+ }
+
+ @Test
+ fun testCanWakeDirectlyToGone_lockscreenDisabledThenEnabled() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false, // Defaults to false.
+ ),
+ canWake
+ )
+
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true)
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ // Still false - isLockScreenDisabled only causes canWakeDirectlyToGone to
+ // update on the next wake/sleep event.
+ false,
+ ),
+ canWake
+ )
+
+ kosmos.powerInteractor.setAsleepForTest()
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ // True since we slept after setting isLockScreenDisabled=true
+ true,
+ ),
+ canWake
+ )
+
+ kosmos.powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ kosmos.powerInteractor.setAsleepForTest()
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ ),
+ canWake
+ )
+
+ whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false)
+ kosmos.powerInteractor.setAwakeForTest()
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ ),
+ canWake
+ )
+ }
+
+ @Test
+ fun testCanWakeDirectlyToGone_wakeAndUnlock() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false, // Defaults to false.
+ ),
+ canWake
+ )
+
+ repository.setBiometricUnlockState(BiometricUnlockMode.WAKE_AND_UNLOCK)
+ runCurrent()
+
+ assertEquals(listOf(false, true), canWake)
+
+ repository.setBiometricUnlockState(BiometricUnlockMode.NONE)
+ runCurrent()
+
+ assertEquals(listOf(false, true, false), canWake)
+ }
+
+ @Test
+ fun testCanWakeDirectlyToGone_andSetsAlarm_ifPowerButtonDoesNotLockImmediately() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false, // Defaults to false.
+ ),
+ canWake
+ )
+
+ repository.setCanIgnoreAuthAndReturnToGone(true)
+ runCurrent()
+
+ assertEquals(listOf(false, true), canWake)
+
+ repository.setCanIgnoreAuthAndReturnToGone(false)
+ runCurrent()
+
+ assertEquals(listOf(false, true, false), canWake)
+ }
+
+ @Test
+ fun testSetsCanIgnoreAuth_andSetsAlarm_whenTimingOut() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false, // Defaults to false.
+ ),
+ canWake
+ )
+
+ whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt()))
+ .thenReturn(-1)
+ kosmos.fakeSettings.putInt(Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 500)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+
+ kosmos.powerInteractor.setAsleepForTest(
+ sleepReason = PowerManager.GO_TO_SLEEP_REASON_TIMEOUT
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ ),
+ canWake
+ )
+
+ verify(kosmos.alarmManager)
+ .setExactAndAllowWhileIdle(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ anyLong(),
+ any(),
+ )
+ }
+
+ @Test
+ fun testCancelsFirstAlarm_onWake_withSecondAlarmSet() =
+ testScope.runTest {
+ val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+ assertEquals(
+ listOf(
+ false, // Defaults to false.
+ ),
+ canWake
+ )
+
+ whenever(kosmos.devicePolicyManager.getMaximumTimeToLock(eq(null), anyInt()))
+ .thenReturn(-1)
+ kosmos.fakeSettings.putInt(Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT, 500)
+
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.GONE,
+ testScope,
+ )
+
+ kosmos.powerInteractor.setAsleepForTest(
+ sleepReason = PowerManager.GO_TO_SLEEP_REASON_TIMEOUT
+ )
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ testScope = testScope,
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ // Timed out, so we can ignore auth/return to GONE.
+ true,
+ ),
+ canWake
+ )
+
+ verify(kosmos.alarmManager)
+ .setExactAndAllowWhileIdle(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ anyLong(),
+ any(),
+ )
+
+ kosmos.powerInteractor.setAwakeForTest()
+ transitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.GONE,
+ testScope = testScope,
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ // Should be canceled by the wakeup, but there would still be an
+ // alarm in flight that should be canceled.
+ false,
+ ),
+ canWake
+ )
+
+ kosmos.powerInteractor.setAsleepForTest(
+ sleepReason = PowerManager.GO_TO_SLEEP_REASON_TIMEOUT
+ )
+ runCurrent()
+
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ // Back to sleep.
+ true,
+ ),
+ canWake
+ )
+
+ // Simulate the first sleep's alarm coming in.
+ lastRegisteredBroadcastReceiver?.onReceive(
+ kosmos.mockedContext,
+ Intent("com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD")
+ )
+ runCurrent()
+
+ // It should not have any effect.
+ assertEquals(
+ listOf(
+ false,
+ true,
+ false,
+ true,
+ ),
+ canWake
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
index c7f4416..0cfc20d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/InWindowLauncherUnlockAnimationManagerTest.kt
@@ -18,17 +18,15 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
-import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.inWindowLauncherUnlockAnimationInteractor
import com.android.systemui.keyguard.ui.view.InWindowLauncherUnlockAnimationManager
+import com.android.systemui.keyguard.ui.viewmodel.InWindowLauncherAnimationViewModel
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testScope
import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationController
-import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
-import dagger.BindsInstance
-import dagger.Component
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -45,10 +43,9 @@
@RunWith(AndroidJUnit4::class)
@kotlinx.coroutines.ExperimentalCoroutinesApi
class InWindowLauncherUnlockAnimationManagerTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var underTest: InWindowLauncherUnlockAnimationManager
-
- private lateinit var testComponent: TestComponent
- private lateinit var testScope: TestScope
+ private val testScope = kosmos.testScope
@Mock private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController
@@ -56,14 +53,14 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
- testComponent =
- DaggerInWindowLauncherUnlockAnimationManagerTest_TestComponent.factory()
- .create(
- test = this,
- )
- underTest = testComponent.underTest
- testScope = testComponent.testScope
-
+ underTest =
+ InWindowLauncherUnlockAnimationManager(
+ kosmos.inWindowLauncherUnlockAnimationInteractor,
+ InWindowLauncherAnimationViewModel(
+ kosmos.inWindowLauncherUnlockAnimationInteractor
+ ),
+ kosmos.applicationCoroutineScope
+ )
underTest.setLauncherUnlockController("launcherClass", launcherUnlockAnimationController)
}
@@ -114,25 +111,4 @@
verifyNoMoreInteractions(launcherUnlockAnimationController)
}
-
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- BiometricsDomainLayerModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent {
- val underTest: InWindowLauncherUnlockAnimationManager
- val testScope: TestScope
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- ): TestComponent
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 69e8f47..9e6a498 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -6,26 +6,23 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.ExpandHelper
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver
+import com.android.systemui.kosmos.testScope
import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
import com.android.systemui.plugins.qs.QS
-import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.qs.ui.adapter.FakeQSSceneAdapter
import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
-import com.android.systemui.shade.data.repository.FakeShadeRepository
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
-import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
+import com.android.systemui.statusbar.disableflags.data.repository.disableFlagsRepository
+import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
@@ -33,17 +30,16 @@
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ScrimController
-import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
-import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.statusbar.policy.configurationController
+import com.android.systemui.statusbar.policy.fakeConfigurationController
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
-import dagger.BindsInstance
-import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -65,8 +61,8 @@
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
return Mockito.anyObject<T>()
@@ -77,15 +73,14 @@
@RunWith(AndroidJUnit4::class)
@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
-
+ private val kosmos =
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
+ }
private lateinit var transitionController: LockscreenShadeTransitionController
- private lateinit var testComponent: TestComponent
- private val configurationController
- get() = testComponent.configurationController
- private val disableFlagsRepository
- get() = testComponent.disableFlagsRepository
- private val testScope
- get() = testComponent.testScope
+ private val configurationController = kosmos.fakeConfigurationController
+ private val disableFlagsRepository = kosmos.fakeDisableFlagsRepository
+ private val testScope = kosmos.testScope
private val qsSceneAdapter = FakeQSSceneAdapter({ mock() })
@@ -134,26 +129,6 @@
whenever(keyguardBypassController.bypassEnabled).thenReturn(false)
whenever(naturalScrollingSettingObserver.isNaturalScrollingEnabled).thenReturn(true)
- testComponent =
- DaggerLockscreenShadeTransitionControllerTest_TestComponent.factory()
- .create(
- test = this,
- featureFlags =
- FakeFeatureFlagsClassicModule {
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- },
- mocks =
- TestMocksModule(
- notificationShadeDepthController = depthController,
- keyguardBypassController = keyguardBypassController,
- mediaHierarchyManager = mediaHierarchyManager,
- notificationLockscreenUserManager = lockScreenUserManager,
- notificationStackScrollLayoutController = nsslController,
- scrimController = scrimController,
- statusBarStateController = statusbarStateController,
- )
- )
-
transitionController =
LockscreenShadeTransitionController(
statusBarStateController = statusbarStateController,
@@ -191,10 +166,10 @@
falsingManager = FalsingManagerFake(),
dumpManager = mock(),
qsTransitionControllerFactory = { qsTransitionController },
- shadeRepository = testComponent.shadeRepository,
- shadeInteractor = testComponent.shadeInteractor,
+ shadeRepository = kosmos.shadeRepository,
+ shadeInteractor = kosmos.shadeInteractor,
splitShadeStateController = ResourcesSplitShadeStateController(),
- shadeLockscreenInteractorLazy = {shadeLockscreenInteractor},
+ shadeLockscreenInteractorLazy = { shadeLockscreenInteractor },
naturalScrollingSettingObserver = naturalScrollingSettingObserver,
lazyQSSceneAdapter = { qsSceneAdapter }
)
@@ -214,387 +189,424 @@
}
@Test
- fun testCantDragDownWhenQSExpanded() {
- assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
- whenever(qS.isFullyCollapsed).thenReturn(false)
- assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
- }
+ fun testCantDragDownWhenQSExpanded() =
+ testScope.runTest {
+ assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+ whenever(qS.isFullyCollapsed).thenReturn(false)
+ assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
+ }
@Test
- fun testCanDragDownInLockedDownShade() {
- whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
- assertFalse("Can drag down in shade locked", transitionController.canDragDown())
- whenever(nsslController.isInLockedDownShade).thenReturn(true)
- assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
- }
+ fun testCanDragDownInLockedDownShade() =
+ testScope.runTest {
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ assertFalse("Can drag down in shade locked", transitionController.canDragDown())
+ whenever(nsslController.isInLockedDownShade).thenReturn(true)
+ assertTrue("Can't drag down in locked down shade", transitionController.canDragDown())
+ }
@Test
- fun testGoingToLockedShade() {
- transitionController.goToLockedShade(null)
- verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- }
+ fun testGoingToLockedShade() =
+ testScope.runTest {
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ }
@Test
- fun testWakingToShadeLockedWhenDozing() {
- whenever(statusbarStateController.isDozing).thenReturn(true)
- transitionController.goToLockedShade(null)
- verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
- }
+ fun testWakingToShadeLockedWhenDozing() =
+ testScope.runTest {
+ whenever(statusbarStateController.isDozing).thenReturn(true)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ assertTrue("Not waking to shade locked", transitionController.isWakingToShadeLocked)
+ }
@Test
- fun testNotWakingToShadeLockedWhenNotDozing() {
- whenever(statusbarStateController.isDozing).thenReturn(false)
- transitionController.goToLockedShade(null)
- verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- assertFalse(
- "Waking to shade locked when not dozing",
- transitionController.isWakingToShadeLocked
- )
- }
-
- @Test
- fun testGoToLockedShadeOnlyOnKeyguard() {
- whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
- transitionController.goToLockedShade(null)
- whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
- transitionController.goToLockedShade(null)
- verify(statusbarStateController, never()).setState(anyInt())
- }
-
- @Test
- fun testDontGoWhenShadeDisabled() {
- disableFlagsRepository.disableFlags.value =
- DisableFlagsModel(
- disable2 = DISABLE2_NOTIFICATION_SHADE,
+ fun testNotWakingToShadeLockedWhenNotDozing() =
+ testScope.runTest {
+ whenever(statusbarStateController.isDozing).thenReturn(false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ assertFalse(
+ "Waking to shade locked when not dozing",
+ transitionController.isWakingToShadeLocked
)
- testScope.runCurrent()
- transitionController.goToLockedShade(null)
- verify(statusbarStateController, never()).setState(anyInt())
- }
+ }
@Test
- fun testUserExpandsViewOnGoingToFullShade() {
- assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
- transitionController.goToLockedShade(row)
- assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
- }
+ fun testGoToLockedShadeOnlyOnKeyguard() =
+ testScope.runTest {
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE_LOCKED)
+ transitionController.goToLockedShade(null)
+ whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ }
@Test
- fun testTriggeringBouncerNoNotificationsOnLockscreen() {
- whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
- transitionController.goToLockedShade(null)
- verify(statusbarStateController, never()).setState(anyInt())
- verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
- verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
- }
+ fun testDontGoWhenShadeDisabled() =
+ testScope.runTest {
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(
+ disable2 = DISABLE2_NOTIFICATION_SHADE,
+ )
+ testScope.runCurrent()
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ }
@Test
- fun testGoToLockedShadeCreatesQSAnimation() {
- transitionController.goToLockedShade(null)
- verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
- assertNotNull(transitionController.dragDownAnimator)
- }
+ fun testUserExpandsViewOnGoingToFullShade() =
+ testScope.runTest {
+ assertFalse("Row shouldn't be user expanded yet", row.isUserExpanded)
+ transitionController.goToLockedShade(row)
+ assertTrue("Row wasn't user expanded on drag down", row.isUserExpanded)
+ }
@Test
- fun testGoToLockedShadeDoesntCreateQSAnimation() {
- transitionController.goToLockedShade(null, needsQSAnimation = false)
- verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
- verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
- assertNull(transitionController.dragDownAnimator)
- }
+ fun testTriggeringBouncerNoNotificationsOnLockscreen() =
+ testScope.runTest {
+ whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController, never()).setState(anyInt())
+ verify(statusbarStateController).setLeaveOpenOnKeyguardHide(true)
+ verify(centralSurfaces)
+ .showBouncerWithDimissAndCancelIfKeyguard(anyObject(), anyObject())
+ }
@Test
- fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() {
- enableSplitShade()
- transitionController.goToLockedShade(null, needsQSAnimation = true)
- verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
- assertNotNull(transitionController.dragDownAnimator)
- }
+ fun testGoToLockedShadeCreatesQSAnimation() =
+ testScope.runTest {
+ transitionController.goToLockedShade(null)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
+ assertNotNull(transitionController.dragDownAnimator)
+ }
@Test
- fun testGoToLockedShadeCancelDoesntLeaveShadeOpenOnKeyguardHide() {
- whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
- whenever(lockScreenUserManager.isLockscreenPublicMode(any())).thenReturn(true)
- transitionController.goToLockedShade(null)
- val captor = argumentCaptor<Runnable>()
- verify(centralSurfaces).showBouncerWithDimissAndCancelIfKeyguard(isNull(), captor.capture())
- captor.value.run()
- verify(statusbarStateController).setLeaveOpenOnKeyguardHide(false)
- }
+ fun testGoToLockedShadeDoesntCreateQSAnimation() =
+ testScope.runTest {
+ transitionController.goToLockedShade(null, needsQSAnimation = false)
+ verify(statusbarStateController).setState(StatusBarState.SHADE_LOCKED)
+ verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
+ assertNull(transitionController.dragDownAnimator)
+ }
@Test
- fun testDragDownAmountDoesntCallOutInLockedDownShade() {
- whenever(nsslController.isInLockedDownShade).thenReturn(true)
- transitionController.dragDownAmount = 10f
- verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
- verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
- verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
- verify(transitionControllerCallback, never())
- .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
- verify(qsTransitionController, never()).dragDownAmount = anyFloat()
- }
+ fun testGoToLockedShadeAlwaysCreatesQSAnimationInSplitShade() =
+ testScope.runTest {
+ enableSplitShade()
+ transitionController.goToLockedShade(null, needsQSAnimation = true)
+ verify(shadeLockscreenInteractor).transitionToExpandedShade(anyLong())
+ assertNotNull(transitionController.dragDownAnimator)
+ }
@Test
- fun testDragDownAmountCallsOut() {
- transitionController.dragDownAmount = 10f
- verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
- verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
- verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
- verify(transitionControllerCallback)
- .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
- verify(qsTransitionController).dragDownAmount = 10f
- verify(depthController).transitionToFullShadeProgress = anyFloat()
- }
+ fun testGoToLockedShadeCancelDoesntLeaveShadeOpenOnKeyguardHide() =
+ testScope.runTest {
+ whenever(lockScreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false)
+ whenever(lockScreenUserManager.isLockscreenPublicMode(any())).thenReturn(true)
+ transitionController.goToLockedShade(null)
+ val captor = argumentCaptor<Runnable>()
+ verify(centralSurfaces)
+ .showBouncerWithDimissAndCancelIfKeyguard(isNull(), captor.capture())
+ captor.value.run()
+ verify(statusbarStateController).setLeaveOpenOnKeyguardHide(false)
+ }
@Test
- fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() {
- context
- .getOrCreateTestableResources()
- .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
- configurationController.notifyConfigurationChanged()
-
- transitionController.dragDownAmount = 10f
-
- verify(depthController).transitionToFullShadeProgress = 0f
- }
+ fun testDragDownAmountDoesntCallOutInLockedDownShade() =
+ testScope.runTest {
+ whenever(nsslController.isInLockedDownShade).thenReturn(true)
+ transitionController.dragDownAmount = 10f
+ verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
+ verify(scrimController, never())
+ .setTransitionToFullShadeProgress(anyFloat(), anyFloat())
+ verify(transitionControllerCallback, never())
+ .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
+ verify(qsTransitionController, never()).dragDownAmount = anyFloat()
+ }
@Test
- fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() {
- context
- .getOrCreateTestableResources()
- .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
- configurationController.notifyConfigurationChanged()
-
- transitionController.dragDownAmount = 10f
-
- verify(depthController).transitionToFullShadeProgress = 0.1f
- }
+ fun testDragDownAmountCallsOut() =
+ testScope.runTest {
+ transitionController.dragDownAmount = 10f
+ verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
+ verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
+ verify(transitionControllerCallback)
+ .setTransitionToFullShadeAmount(anyFloat(), anyBoolean(), anyLong())
+ verify(qsTransitionController).dragDownAmount = 10f
+ verify(depthController).transitionToFullShadeProgress = anyFloat()
+ }
@Test
- fun setDragAmount_setsKeyguardTransitionProgress() {
- transitionController.dragDownAmount = 10f
+ fun testDragDownAmount_depthDistanceIsZero_setsProgressToZero() =
+ testScope.runTest {
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 0)
+ configurationController.notifyConfigurationChanged()
- verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), anyInt())
- }
+ transitionController.dragDownAmount = 10f
+
+ verify(depthController).transitionToFullShadeProgress = 0f
+ }
@Test
- fun setDragAmount_setsKeyguardAlphaBasedOnDistance() {
- val alphaDistance =
- context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
- )
- transitionController.dragDownAmount = 10f
+ fun testDragDownAmount_depthDistanceNonZero_setsProgressBasedOnDistance() =
+ testScope.runTest {
+ context
+ .getOrCreateTestableResources()
+ .addOverride(R.dimen.lockscreen_shade_depth_controller_transition_distance, 100)
+ configurationController.notifyConfigurationChanged()
- val expectedAlpha = 1 - 10f / alphaDistance
- verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
- }
+ transitionController.dragDownAmount = 10f
+
+ verify(depthController).transitionToFullShadeProgress = 0.1f
+ }
@Test
- fun setDragAmount_notInSplitShade_setsKeyguardTranslationToZero() {
- val mediaTranslationY = 123
- disableSplitShade()
- whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
- whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
- .thenReturn(mediaTranslationY)
+ fun setDragAmount_setsKeyguardTransitionProgress() =
+ testScope.runTest {
+ transitionController.dragDownAmount = 10f
- transitionController.dragDownAmount = 10f
-
- verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), eq(0))
- }
+ verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), anyInt())
+ }
@Test
- fun setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslation() {
- val mediaTranslationY = 123
- enableSplitShade()
- whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
- whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
- .thenReturn(mediaTranslationY)
+ fun setDragAmount_setsKeyguardAlphaBasedOnDistance() =
+ testScope.runTest {
+ val alphaDistance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ )
+ transitionController.dragDownAmount = 10f
- transitionController.dragDownAmount = 10f
+ val expectedAlpha = 1 - 10f / alphaDistance
+ verify(shadeLockscreenInteractor)
+ .setKeyguardTransitionProgress(eq(expectedAlpha), anyInt())
+ }
- verify(shadeLockscreenInteractor)
+ @Test
+ fun setDragAmount_notInSplitShade_setsKeyguardTranslationToZero() =
+ testScope.runTest {
+ val mediaTranslationY = 123
+ disableSplitShade()
+ whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
+ whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
+ .thenReturn(mediaTranslationY)
+
+ transitionController.dragDownAmount = 10f
+
+ verify(shadeLockscreenInteractor).setKeyguardTransitionProgress(anyFloat(), eq(0))
+ }
+
+ @Test
+ fun setDragAmount_inSplitShade_setsKeyguardTranslationBasedOnMediaTranslation() =
+ testScope.runTest {
+ val mediaTranslationY = 123
+ enableSplitShade()
+ whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(true)
+ whenever(mediaHierarchyManager.getGuidedTransformationTranslationY())
+ .thenReturn(mediaTranslationY)
+
+ transitionController.dragDownAmount = 10f
+
+ verify(shadeLockscreenInteractor)
.setKeyguardTransitionProgress(anyFloat(), eq(mediaTranslationY))
- }
+ }
@Test
- fun setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistance() {
- enableSplitShade()
- whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(false)
- whenever(mediaHierarchyManager.getGuidedTransformationTranslationY()).thenReturn(123)
+ fun setDragAmount_inSplitShade_mediaNotShowing_setsKeyguardTranslationBasedOnDistance() =
+ testScope.runTest {
+ enableSplitShade()
+ whenever(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).thenReturn(false)
+ whenever(mediaHierarchyManager.getGuidedTransformationTranslationY()).thenReturn(123)
- transitionController.dragDownAmount = 10f
+ transitionController.dragDownAmount = 10f
- val distance =
- context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_keyguard_transition_distance
+ val distance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_keyguard_transition_distance
+ )
+ val offset =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
+ )
+ val expectedTranslation = 10f / distance * offset
+ verify(shadeLockscreenInteractor)
+ .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
+ }
+
+ @Test
+ fun setDragDownAmount_setsValueOnMediaHierarchyManager() =
+ testScope.runTest {
+ transitionController.dragDownAmount = 10f
+
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+ }
+
+ @Test
+ fun setDragAmount_setsScrimProgressBasedOnScrimDistance() =
+ testScope.runTest {
+ val distance = 10
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_scrim_transition_distance,
+ distance
)
- val offset =
- context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_keyguard_transition_vertical_offset
+ configurationController.notifyConfigurationChanged()
+
+ transitionController.dragDownAmount = 5f
+
+ verify(scrimController)
+ .transitionToFullShadeProgress(
+ progress = eq(0.5f),
+ lockScreenNotificationsProgress = anyFloat()
+ )
+ }
+
+ @Test
+ fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() =
+ testScope.runTest {
+ val distance = 100
+ val delay = 10
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
)
- val expectedTranslation = 10f / distance * offset
- verify(shadeLockscreenInteractor)
- .setKeyguardTransitionProgress(anyFloat(), eq(expectedTranslation.toInt()))
- }
-
- @Test
- fun setDragDownAmount_setsValueOnMediaHierarchyManager() {
- transitionController.dragDownAmount = 10f
-
- verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
- }
-
- @Test
- fun setDragAmount_setsScrimProgressBasedOnScrimDistance() {
- val distance = 10
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_scrim_transition_distance,
- distance
- )
- configurationController.notifyConfigurationChanged()
-
- transitionController.dragDownAmount = 5f
-
- verify(scrimController)
- .transitionToFullShadeProgress(
- progress = eq(0.5f),
- lockScreenNotificationsProgress = anyFloat()
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
)
- }
+ configurationController.notifyConfigurationChanged()
+
+ transitionController.dragDownAmount = 20f
+
+ verify(scrimController)
+ .transitionToFullShadeProgress(
+ progress = anyFloat(),
+ lockScreenNotificationsProgress = eq(0.1f)
+ )
+ }
@Test
- fun setDragAmount_setsNotificationsScrimProgressBasedOnNotificationsScrimDistanceAndDelay() {
- val distance = 100
- val delay = 10
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
- distance
- )
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
- delay
- )
- configurationController.notifyConfigurationChanged()
-
- transitionController.dragDownAmount = 20f
-
- verify(scrimController)
- .transitionToFullShadeProgress(
- progress = anyFloat(),
- lockScreenNotificationsProgress = eq(0.1f)
+ fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() =
+ testScope.runTest {
+ val distance = 100
+ val delay = 50
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
)
- }
-
- @Test
- fun setDragAmount_dragAmountLessThanNotifDelayDistance_setsNotificationsScrimProgressToZero() {
- val distance = 100
- val delay = 50
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
- distance
- )
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
- delay
- )
- configurationController.notifyConfigurationChanged()
-
- transitionController.dragDownAmount = 20f
-
- verify(scrimController)
- .transitionToFullShadeProgress(
- progress = anyFloat(),
- lockScreenNotificationsProgress = eq(0f)
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
)
- }
+ configurationController.notifyConfigurationChanged()
+
+ transitionController.dragDownAmount = 20f
+
+ verify(scrimController)
+ .transitionToFullShadeProgress(
+ progress = anyFloat(),
+ lockScreenNotificationsProgress = eq(0f)
+ )
+ }
@Test
- fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() {
- val distance = 100
- val delay = 50
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
- distance
- )
- context.orCreateTestableResources.addOverride(
- R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
- delay
- )
- configurationController.notifyConfigurationChanged()
-
- transitionController.dragDownAmount = 999999f
-
- verify(scrimController)
- .transitionToFullShadeProgress(
- progress = anyFloat(),
- lockScreenNotificationsProgress = eq(1f)
+ fun setDragAmount_dragAmountMoreThanTotalDistance_setsNotificationsScrimProgressToOne() =
+ testScope.runTest {
+ val distance = 100
+ val delay = 50
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_notifications_scrim_transition_distance,
+ distance
)
- }
-
- @Test
- fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() {
- enableSplitShade()
-
- transitionController.dragDownAmount = 10f
-
- verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
- }
-
- @Test
- fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() {
- disableSplitShade()
-
- transitionController.dragDownAmount = 10f
-
- verify(singleShadeOverScroller).expansionDragDownAmount = 10f
- verifyZeroInteractions(splitShadeOverScroller)
- }
-
- @Test
- fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() {
- enableSplitShade()
-
- transitionController.dragDownAmount = 10f
-
- verify(splitShadeOverScroller).expansionDragDownAmount = 10f
- verifyZeroInteractions(singleShadeOverScroller)
- }
-
- @Test
- fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() {
- val alphaDistance =
- context.resources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ context.orCreateTestableResources.addOverride(
+ R.dimen.lockscreen_shade_notifications_scrim_transition_delay,
+ delay
)
- val dragDownAmount = 10f
- enableSplitShade()
+ configurationController.notifyConfigurationChanged()
- transitionController.dragDownAmount = dragDownAmount
+ transitionController.dragDownAmount = 999999f
- val expectedAlpha = 1 - dragDownAmount / alphaDistance
- verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(expectedAlpha)
- }
+ verify(scrimController)
+ .transitionToFullShadeProgress(
+ progress = anyFloat(),
+ lockScreenNotificationsProgress = eq(1f)
+ )
+ }
@Test
- fun setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOne() {
- disableSplitShade()
+ fun setDragDownAmount_inSplitShade_setsValueOnMediaHierarchyManager() =
+ testScope.runTest {
+ enableSplitShade()
- transitionController.dragDownAmount = 10f
+ transitionController.dragDownAmount = 10f
- verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
- }
+ verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
+ }
@Test
- fun nullQs_canDragDownFromAdapter() {
- transitionController.qS = null
+ fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() =
+ testScope.runTest {
+ disableSplitShade()
- qsSceneAdapter.isQsFullyCollapsed = true
- assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
- qsSceneAdapter.isQsFullyCollapsed = false
- assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
- }
+ transitionController.dragDownAmount = 10f
+
+ verify(singleShadeOverScroller).expansionDragDownAmount = 10f
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ @Test
+ fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() =
+ testScope.runTest {
+ enableSplitShade()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(splitShadeOverScroller).expansionDragDownAmount = 10f
+ verifyZeroInteractions(singleShadeOverScroller)
+ }
+
+ @Test
+ fun setDragDownAmount_inSplitShade_setsKeyguardStatusBarAlphaBasedOnDistance() =
+ testScope.runTest {
+ val alphaDistance =
+ context.resources.getDimensionPixelSize(
+ R.dimen.lockscreen_shade_npvc_keyguard_content_alpha_transition_distance
+ )
+ val dragDownAmount = 10f
+ enableSplitShade()
+
+ transitionController.dragDownAmount = dragDownAmount
+
+ val expectedAlpha = 1 - dragDownAmount / alphaDistance
+ verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(expectedAlpha)
+ }
+
+ @Test
+ fun setDragDownAmount_notInSplitShade_setsKeyguardStatusBarAlphaToMinusOne() =
+ testScope.runTest {
+ disableSplitShade()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(shadeLockscreenInteractor).setKeyguardStatusBarAlpha(-1f)
+ }
+
+ @Test
+ fun nullQs_canDragDownFromAdapter() =
+ testScope.runTest {
+ transitionController.qS = null
+
+ qsSceneAdapter.isQsFullyCollapsed = true
+ assertTrue("Can't drag down on keyguard", transitionController.canDragDown())
+ qsSceneAdapter.isQsFullyCollapsed = false
+ assertFalse("Can drag down when QS is expanded", transitionController.canDragDown())
+ }
private fun enableSplitShade() {
setSplitShadeEnabled(true)
@@ -619,32 +631,4 @@
) {
setTransitionToFullShadeProgress(progress, lockScreenNotificationsProgress)
}
-
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- UserDomainLayerModule::class,
- BiometricsDomainLayerModule::class,
- ]
- )
- interface TestComponent {
-
- val configurationController: FakeConfigurationController
- val disableFlagsRepository: FakeDisableFlagsRepository
- val powerInteractor: PowerInteractor
- val shadeInteractor: ShadeInteractor
- val shadeRepository: FakeShadeRepository
- val testScope: TestScope
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
- ): TestComponent
- }
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index 26f5370..f07303e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -17,21 +17,19 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.deviceentry.data.repository.FakeDeviceEntryRepository
-import com.android.systemui.runTest
-import com.android.systemui.statusbar.data.repository.NotificationListenerSettingsRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.statusbar.data.repository.notificationListenerSettingsRepository
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
-import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
-import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
-import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.notificationsKeyguardViewStateRepository
+import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.shared.byIsAmbient
import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
import com.android.systemui.statusbar.notification.shared.byIsPulsing
@@ -39,15 +37,15 @@
import com.android.systemui.statusbar.notification.shared.byIsSilent
import com.android.systemui.statusbar.notification.shared.byIsSuppressedFromStatusBar
import com.android.systemui.statusbar.notification.shared.byKey
-import com.android.systemui.user.domain.UserDomainLayerModule
+import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.eq
-import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
-import com.android.wm.shell.bubbles.Bubbles
+import com.android.wm.shell.bubbles.bubbles
+import com.android.wm.shell.bubbles.bubblesOptional
import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
-import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,29 +53,22 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotificationIconsInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val activeNotificationListRepository = kosmos.activeNotificationListRepository
+ private val notificationsKeyguardInteractor = kosmos.notificationsKeyguardInteractor
- private val bubbles: Bubbles = mock()
-
- @Component(modules = [SysUITestModule::class])
- @SysUISingleton
- interface TestComponent : SysUITestComponent<NotificationIconsInteractor> {
-
- val activeNotificationListRepository: ActiveNotificationListRepository
- val notificationsKeyguardInteractor: NotificationsKeyguardInteractor
-
- @Component.Factory
- interface Factory {
- fun create(@BindsInstance test: SysuiTestCase, mocks: TestMocksModule): TestComponent
- }
- }
-
- val testComponent: TestComponent =
- DaggerNotificationIconsInteractorTest_TestComponent.factory()
- .create(test = this, mocks = TestMocksModule(bubbles = Optional.of(bubbles)))
+ private val underTest =
+ NotificationIconsInteractor(
+ kosmos.activeNotificationsInteractor,
+ kosmos.bubblesOptional,
+ kosmos.headsUpNotificationIconInteractor,
+ kosmos.notificationsKeyguardViewStateRepository
+ )
@Before
fun setup() {
- testComponent.apply {
+ testScope.apply {
activeNotificationListRepository.activeNotifications.value =
ActiveNotificationsStore.Builder()
.apply { testIcons.forEach(::addIndividualNotif) }
@@ -87,22 +78,22 @@
@Test
fun filteredEntrySet() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.filteredNotifSet())
assertThat(filteredSet).containsExactlyElementsIn(testIcons)
}
@Test
fun filteredEntrySet_noExpandedBubbles() =
- testComponent.runTest {
- whenever(bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
+ testScope.runTest {
+ whenever(kosmos.bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
val filteredSet by collectLastValue(underTest.filteredNotifSet())
assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1")
}
@Test
fun filteredEntrySet_noAmbient() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.filteredNotifSet(showAmbient = false))
assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true)
assertThat(filteredSet)
@@ -112,21 +103,21 @@
@Test
fun filteredEntrySet_noLowPriority() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.filteredNotifSet(showLowPriority = false))
assertThat(filteredSet).comparingElementsUsing(byIsSilent).doesNotContain(true)
}
@Test
fun filteredEntrySet_noDismissed() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.filteredNotifSet(showDismissed = false))
assertThat(filteredSet).comparingElementsUsing(byIsRowDismissed).doesNotContain(true)
}
@Test
fun filteredEntrySet_noRepliedMessages() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by
collectLastValue(underTest.filteredNotifSet(showRepliedMessages = false))
assertThat(filteredSet)
@@ -136,7 +127,7 @@
@Test
fun filteredEntrySet_noPulsing_notifsNotFullyHidden() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false))
notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true)
@@ -144,65 +135,46 @@
@Test
fun filteredEntrySet_noPulsing_notifsFullyHidden() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.filteredNotifSet(showPulsing = false))
notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
}
}
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class AlwaysOnDisplayNotificationIconsInteractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
- private val bubbles: Bubbles = mock()
-
- @Component(
- modules =
- [
- SysUITestModule::class,
- BiometricsDomainLayerModule::class,
- UserDomainLayerModule::class,
- ]
- )
- @SysUISingleton
- interface TestComponent : SysUITestComponent<AlwaysOnDisplayNotificationIconsInteractor> {
-
- val activeNotificationListRepository: ActiveNotificationListRepository
- val deviceEntryRepository: FakeDeviceEntryRepository
- val notificationsKeyguardInteractor: NotificationsKeyguardInteractor
-
- @Component.Factory
- interface Factory {
- fun create(@BindsInstance test: SysuiTestCase, mocks: TestMocksModule): TestComponent
- }
- }
-
- private val testComponent: TestComponent =
- DaggerAlwaysOnDisplayNotificationIconsInteractorTest_TestComponent.factory()
- .create(test = this, mocks = TestMocksModule(bubbles = Optional.of(bubbles)))
+ private val underTest =
+ AlwaysOnDisplayNotificationIconsInteractor(
+ kosmos.testDispatcher,
+ kosmos.deviceEntryInteractor,
+ kosmos.notificationIconsInteractor,
+ )
@Before
fun setup() {
- testComponent.apply {
- activeNotificationListRepository.activeNotifications.value =
- ActiveNotificationsStore.Builder()
- .apply { testIcons.forEach(::addIndividualNotif) }
- .build()
- }
+ kosmos.activeNotificationListRepository.activeNotifications.value =
+ ActiveNotificationsStore.Builder()
+ .apply { testIcons.forEach(::addIndividualNotif) }
+ .build()
}
@Test
fun filteredEntrySet_noExpandedBubbles() =
- testComponent.runTest {
- whenever(bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
+ testScope.runTest {
+ whenever(kosmos.bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
val filteredSet by collectLastValue(underTest.aodNotifs)
assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1")
}
@Test
fun filteredEntrySet_noAmbient() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true)
assertThat(filteredSet)
@@ -212,14 +184,14 @@
@Test
fun filteredEntrySet_noDismissed() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
assertThat(filteredSet).comparingElementsUsing(byIsRowDismissed).doesNotContain(true)
}
@Test
fun filteredEntrySet_noRepliedMessages() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
assertThat(filteredSet)
.comparingElementsUsing(byIsLastMessageFromReply)
@@ -228,37 +200,37 @@
@Test
fun filteredEntrySet_showPulsing_notifsNotFullyHidden_bypassDisabled() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
- deviceEntryRepository.setBypassEnabled(false)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
+ kosmos.fakeDeviceEntryRepository.setBypassEnabled(false)
+ kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
}
@Test
fun filteredEntrySet_showPulsing_notifsFullyHidden_bypassDisabled() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
- deviceEntryRepository.setBypassEnabled(false)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+ kosmos.fakeDeviceEntryRepository.setBypassEnabled(false)
+ kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
}
@Test
fun filteredEntrySet_noPulsing_notifsNotFullyHidden_bypassEnabled() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
- deviceEntryRepository.setBypassEnabled(true)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
+ kosmos.fakeDeviceEntryRepository.setBypassEnabled(true)
+ kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(false)
assertThat(filteredSet).comparingElementsUsing(byIsPulsing).doesNotContain(true)
}
@Test
fun filteredEntrySet_showPulsing_notifsFullyHidden_bypassEnabled() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.aodNotifs)
- deviceEntryRepository.setBypassEnabled(true)
- notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
+ kosmos.fakeDeviceEntryRepository.setBypassEnabled(true)
+ kosmos.notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
assertThat(filteredSet).comparingElementsUsing(byIsPulsing).contains(true)
}
}
@@ -266,32 +238,19 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class StatusBarNotificationIconsInteractorTest : SysuiTestCase() {
-
- private val bubbles: Bubbles = mock()
-
- @Component(modules = [SysUITestModule::class])
- @SysUISingleton
- interface TestComponent : SysUITestComponent<StatusBarNotificationIconsInteractor> {
-
- val activeNotificationListRepository: ActiveNotificationListRepository
- val headsUpIconsInteractor: HeadsUpNotificationIconInteractor
- val notificationsKeyguardInteractor: NotificationsKeyguardInteractor
- val notificationListenerSettingsRepository: NotificationListenerSettingsRepository
-
- @Component.Factory
- interface Factory {
- fun create(@BindsInstance test: SysuiTestCase, mocks: TestMocksModule): TestComponent
- }
- }
-
- val testComponent: TestComponent =
- DaggerStatusBarNotificationIconsInteractorTest_TestComponent.factory()
- .create(test = this, mocks = TestMocksModule(bubbles = Optional.of(bubbles)))
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val underTest =
+ StatusBarNotificationIconsInteractor(
+ kosmos.testDispatcher,
+ kosmos.notificationIconsInteractor,
+ kosmos.notificationListenerSettingsRepository,
+ )
@Before
fun setup() {
- testComponent.apply {
- activeNotificationListRepository.activeNotifications.value =
+ testScope.apply {
+ kosmos.activeNotificationListRepository.activeNotifications.value =
ActiveNotificationsStore.Builder()
.apply { testIcons.forEach(::addIndividualNotif) }
.build()
@@ -300,15 +259,15 @@
@Test
fun filteredEntrySet_noExpandedBubbles() =
- testComponent.runTest {
- whenever(bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
+ testScope.runTest {
+ whenever(kosmos.bubbles.isBubbleExpanded(eq("notif1"))).thenReturn(true)
val filteredSet by collectLastValue(underTest.statusBarNotifs)
assertThat(filteredSet).comparingElementsUsing(byKey).doesNotContain("notif1")
}
@Test
fun filteredEntrySet_noAmbient() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.statusBarNotifs)
assertThat(filteredSet).comparingElementsUsing(byIsAmbient).doesNotContain(true)
assertThat(filteredSet)
@@ -318,30 +277,30 @@
@Test
fun filteredEntrySet_noLowPriority_whenDontShowSilentIcons() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.statusBarNotifs)
- notificationListenerSettingsRepository.showSilentStatusIcons.value = false
+ kosmos.notificationListenerSettingsRepository.showSilentStatusIcons.value = false
assertThat(filteredSet).comparingElementsUsing(byIsSilent).doesNotContain(true)
}
@Test
fun filteredEntrySet_showLowPriority_whenShowSilentIcons() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.statusBarNotifs)
- notificationListenerSettingsRepository.showSilentStatusIcons.value = true
+ kosmos.notificationListenerSettingsRepository.showSilentStatusIcons.value = true
assertThat(filteredSet).comparingElementsUsing(byIsSilent).contains(true)
}
@Test
fun filteredEntrySet_noDismissed() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.statusBarNotifs)
assertThat(filteredSet).comparingElementsUsing(byIsRowDismissed).doesNotContain(true)
}
@Test
fun filteredEntrySet_noRepliedMessages() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.statusBarNotifs)
assertThat(filteredSet)
.comparingElementsUsing(byIsLastMessageFromReply)
@@ -350,9 +309,9 @@
@Test
fun filteredEntrySet_includesIsolatedIcon() =
- testComponent.runTest {
+ testScope.runTest {
val filteredSet by collectLastValue(underTest.statusBarNotifs)
- headsUpIconsInteractor.setIsolatedIconNotificationKey("notif5")
+ kosmos.headsUpNotificationIconInteractor.setIsolatedIconNotificationKey("notif5")
assertThat(filteredSet).comparingElementsUsing(byKey).contains("notif5")
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
index 894e02e..1f4e80e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerAlwaysOnDisplayViewModelTest.kt
@@ -16,111 +16,81 @@
package com.android.systemui.statusbar.notification.icon.ui.viewmodel
+import android.content.res.mainResources
import android.platform.test.annotations.DisableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_NEW_AOD_TRANSITION
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.power.data.repository.FakePowerRepository
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.statusbar.phone.DozeParameters
-import com.android.systemui.statusbar.phone.ScreenOffAnimationController
-import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository
-import com.android.systemui.user.domain.UserDomainLayerModule
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.icon.domain.interactor.alwaysOnDisplayNotificationIconsInteractor
+import com.android.systemui.statusbar.phone.dozeParameters
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class NotificationIconContainerAlwaysOnDisplayViewModelTest : SysuiTestCase() {
-
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- BiometricsDomainLayerModule::class,
- UserDomainLayerModule::class,
- ]
- )
- interface TestComponent :
- SysUITestComponent<NotificationIconContainerAlwaysOnDisplayViewModel> {
-
- val deviceProvisioningRepository: FakeDeviceProvisioningRepository
- val keyguardRepository: FakeKeyguardRepository
- val keyguardTransitionRepository: FakeKeyguardTransitionRepository
- val powerRepository: FakePowerRepository
-
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- mocks: TestMocksModule,
- featureFlags: FakeFeatureFlagsClassicModule,
- ): TestComponent
+ private val kosmos =
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, value = false) }
}
- }
- private val dozeParams: DozeParameters = mock()
- private val screenOffAnimController: ScreenOffAnimationController = mock()
-
- private val testComponent: TestComponent =
- DaggerNotificationIconContainerAlwaysOnDisplayViewModelTest_TestComponent.factory()
- .create(
- test = this,
- featureFlags =
- FakeFeatureFlagsClassicModule {
- set(Flags.FULL_SCREEN_USER_SWITCHER, value = false)
- },
- mocks =
- TestMocksModule(
- dozeParameters = dozeParams,
- screenOffAnimationController = screenOffAnimController,
- ),
- )
+ val underTest =
+ NotificationIconContainerAlwaysOnDisplayViewModel(
+ kosmos.testDispatcher,
+ kosmos.alwaysOnDisplayNotificationIconsInteractor,
+ kosmos.keyguardInteractor,
+ kosmos.keyguardTransitionInteractor,
+ kosmos.mainResources,
+ kosmos.shadeInteractor,
+ )
+ val testScope = kosmos.testScope
+ val keyguardRepository = kosmos.fakeKeyguardRepository
+ val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+ val powerRepository = kosmos.fakePowerRepository
@Before
fun setup() {
- testComponent.apply {
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- powerRepository.updateWakefulness(
- rawState = WakefulnessState.AWAKE,
- lastWakeReason = WakeSleepReason.OTHER,
- lastSleepReason = WakeSleepReason.OTHER,
- )
- }
+ keyguardRepository.setKeyguardShowing(true)
+ keyguardRepository.setKeyguardOccluded(false)
+ kosmos.fakePowerRepository.updateWakefulness(
+ rawState = WakefulnessState.AWAKE,
+ lastWakeReason = WakeSleepReason.OTHER,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION)
}
@Test
fun animationsEnabled_isFalse_whenDeviceAsleepAndNotPulsing() =
- testComponent.runTest {
+ testScope.runTest {
powerRepository.updateWakefulness(
rawState = WakefulnessState.ASLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -143,7 +113,7 @@
@Test
fun animationsEnabled_isTrue_whenDeviceAsleepAndPulsing() =
- testComponent.runTest {
+ testScope.runTest {
powerRepository.updateWakefulness(
rawState = WakefulnessState.ASLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -166,7 +136,7 @@
@Test
fun animationsEnabled_isFalse_whenStartingToSleepAndNotControlScreenOff() =
- testComponent.runTest {
+ testScope.runTest {
powerRepository.updateWakefulness(
rawState = WakefulnessState.STARTING_TO_SLEEP,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -179,7 +149,7 @@
transitionState = TransitionState.STARTED,
)
)
- whenever(dozeParams.shouldControlScreenOff()).thenReturn(false)
+ whenever(kosmos.dozeParameters.shouldControlScreenOff()).thenReturn(false)
val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
runCurrent()
assertThat(animationsEnabled).isFalse()
@@ -187,7 +157,7 @@
@Test
fun animationsEnabled_isTrue_whenStartingToSleepAndControlScreenOff() =
- testComponent.runTest {
+ testScope.runTest {
val animationsEnabled by collectLastValue(underTest.areContainerChangesAnimated)
assertThat(animationsEnabled).isTrue()
@@ -203,13 +173,13 @@
transitionState = TransitionState.STARTED,
)
)
- whenever(dozeParams.shouldControlScreenOff()).thenReturn(true)
+ whenever(kosmos.dozeParameters.shouldControlScreenOff()).thenReturn(true)
assertThat(animationsEnabled).isTrue()
}
@Test
fun animationsEnabled_isTrue_whenNotAsleep() =
- testComponent.runTest {
+ testScope.runTest {
powerRepository.updateWakefulness(
rawState = WakefulnessState.AWAKE,
lastWakeReason = WakeSleepReason.POWER_BUTTON,
@@ -228,7 +198,7 @@
@Test
@DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
fun animationsEnabled_isTrue_whenKeyguardIsShowing() =
- testComponent.runTest {
+ testScope.runTest {
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
transitionState = TransitionState.STARTED,
@@ -257,7 +227,7 @@
@Test
fun tintAlpha_isZero_whenNotOnAodOrDozing() =
- testComponent.runTest {
+ testScope.runTest {
val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
@@ -271,7 +241,7 @@
@Test
fun tintAlpha_isOne_whenOnAod() =
- testComponent.runTest {
+ testScope.runTest {
val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
@@ -285,7 +255,7 @@
@Test
fun tintAlpha_isOne_whenDozing() =
- testComponent.runTest {
+ testScope.runTest {
val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
keyguardTransitionRepository.sendTransitionSteps(
@@ -298,7 +268,7 @@
@Test
fun tintAlpha_isOne_whenTransitionFromAodToDoze() =
- testComponent.runTest {
+ testScope.runTest {
keyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.GONE,
to = KeyguardState.AOD,
@@ -332,7 +302,7 @@
@Test
fun tintAlpha_isFraction_midTransitionToAod() =
- testComponent.runTest {
+ testScope.runTest {
val tintAlpha by collectLastValue(underTest.tintAlpha)
runCurrent()
@@ -361,7 +331,7 @@
@Test
fun iconAnimationsEnabled_whenOnLockScreen() =
- testComponent.runTest {
+ testScope.runTest {
val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
runCurrent()
@@ -376,7 +346,7 @@
@Test
fun iconAnimationsDisabled_whenOnAod() =
- testComponent.runTest {
+ testScope.runTest {
val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
runCurrent()
@@ -391,7 +361,7 @@
@Test
fun iconAnimationsDisabled_whenDozing() =
- testComponent.runTest {
+ testScope.runTest {
val iconAnimationsEnabled by collectLastValue(underTest.areIconAnimationsEnabled)
runCurrent()
diff --git a/packages/SystemUI/tests/utils/src/android/app/admin/AlarmManagerKosmos.kt b/packages/SystemUI/tests/utils/src/android/app/admin/AlarmManagerKosmos.kt
new file mode 100644
index 0000000..a7b5873
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/android/app/admin/AlarmManagerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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 android.app.admin
+
+import android.app.AlarmManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.alarmManager by Kosmos.Fixture { mock<AlarmManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/app/admin/DevicePolicyManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/app/admin/DevicePolicyManagerKosmos.kt
new file mode 100644
index 0000000..f51e122
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/app/admin/DevicePolicyManagerKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 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.app.admin
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+val Kosmos.devicePolicyManager by Kosmos.Fixture { mock<android.app.admin.DevicePolicyManager>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/internal/widget/LockPatternUtilsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/internal/widget/LockPatternUtilsKosmos.kt
index d9ea5e9..b511270 100644
--- a/packages/SystemUI/tests/utils/src/com/android/internal/widget/LockPatternUtilsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/internal/widget/LockPatternUtilsKosmos.kt
@@ -16,7 +16,14 @@
package com.android.internal.widget
+import android.app.admin.devicePolicyManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
-var Kosmos.lockPatternUtils by Kosmos.Fixture { mock<LockPatternUtils>() }
+var Kosmos.lockPatternUtils by
+ Kosmos.Fixture {
+ mock<LockPatternUtils>().apply {
+ whenever(this.devicePolicyManager).thenReturn(this@Fixture.devicePolicyManager)
+ }
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 5bae6ec..87143ef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -135,6 +135,9 @@
private var isShowKeyguardWhenReenabled: Boolean = false
+ private val _canIgnoreAuthAndReturnToGone = MutableStateFlow(false)
+ override val canIgnoreAuthAndReturnToGone = _canIgnoreAuthAndReturnToGone.asStateFlow()
+
override fun setQuickSettingsVisible(isVisible: Boolean) {
_isQuickSettingsVisible.value = isVisible
}
@@ -278,6 +281,10 @@
override fun isShowKeyguardWhenReenabled(): Boolean {
return isShowKeyguardWhenReenabled
}
+
+ override fun setCanIgnoreAuthAndReturnToGone(canWake: Boolean) {
+ _canIgnoreAuthAndReturnToGone.value = canWake
+ }
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
index ae138c8..ef789d1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorKosmos.kt
@@ -37,5 +37,6 @@
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
deviceEntryRepository = deviceEntryRepository,
+ wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
index e7e007f..446652c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorKosmos.kt
@@ -39,5 +39,6 @@
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
deviceEntryRepository = deviceEntryRepository,
+ wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
index a9be06d..6c3de44 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractorKosmos.kt
@@ -16,13 +16,16 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+@OptIn(ExperimentalCoroutinesApi::class)
var Kosmos.fromDreamingTransitionInteractor by
Kosmos.Fixture {
FromDreamingTransitionInteractor(
@@ -36,5 +39,6 @@
glanceableHubTransitions = glanceableHubTransitions,
powerInteractor = powerInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ deviceEntryInteractor = deviceEntryInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
new file mode 100644
index 0000000..63e168d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorKosmos.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.app.admin.alarmManager
+import android.content.mockedContext
+import com.android.internal.widget.lockPatternUtils
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.power.domain.interactor.powerInteractor
+import com.android.systemui.user.domain.interactor.selectedUserInteractor
+import com.android.systemui.util.settings.fakeSettings
+import com.android.systemui.util.time.systemClock
+
+val Kosmos.keyguardWakeDirectlyToGoneInteractor by
+ Kosmos.Fixture {
+ KeyguardWakeDirectlyToGoneInteractor(
+ applicationCoroutineScope,
+ mockedContext,
+ fakeKeyguardRepository,
+ systemClock,
+ alarmManager,
+ keyguardTransitionInteractor,
+ powerInteractor,
+ fakeSettings,
+ lockPatternUtils,
+ fakeSettings,
+ selectedUserInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index bd9c0be..8bb2fce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
@@ -25,6 +26,7 @@
Kosmos.Fixture {
WindowManagerLockscreenVisibilityInteractor(
keyguardInteractor = keyguardInteractor,
+ transitionRepository = keyguardTransitionRepository,
transitionInteractor = keyguardTransitionInteractor,
surfaceBehindInteractor = keyguardSurfaceBehindInteractor,
fromLockscreenInteractor = fromLockscreenTransitionInteractor,
@@ -33,5 +35,6 @@
notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
sceneInteractor = { sceneInteractor },
deviceEntryInteractor = { deviceEntryInteractor },
+ wakeToGoneInteractor = keyguardWakeDirectlyToGoneInteractor,
)
}