Provide API for accessing the current state of secure camera.
Secure camera refers to camera app launched over the lockscreen.
This piece of information is required to control face auth behavior:
1. Face auth shouldn't run when secure camera is launched
2. Face auth should run when secure camera is launched but when UDFPS bouncer is shown.
Bug: 262838215
Test: atest KeyguardRepositoryTest
Test: atest SecureCameraInteractorTest
Change-Id: I8d6d43b86c85152f6be8e7f9b0deb13a8a46cc1b
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index ea079a9..c05e2a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -24,8 +24,8 @@
import android.text.format.DateFormat
import android.util.TypedValue
import android.view.View
-import android.widget.FrameLayout
import android.view.ViewTreeObserver
+import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
@@ -40,8 +40,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.log.dagger.KeyguardLargeClockLog
+import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockTickRate
@@ -53,22 +53,24 @@
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
+import java.util.Locale
+import java.util.TimeZone
+import java.util.concurrent.Executor
+import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
-import java.util.Locale
-import java.util.TimeZone
-import java.util.concurrent.Executor
-import javax.inject.Inject
/**
* Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/
-open class ClockEventController @Inject constructor(
+open class ClockEventController
+@Inject
+constructor(
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val broadcastDispatcher: BroadcastDispatcher,
@@ -115,52 +117,59 @@
private var disposableHandle: DisposableHandle? = null
private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
- private val mLayoutChangedListener = object : View.OnLayoutChangeListener {
- private var currentSmallClockView: View? = null
- private var currentLargeClockView: View? = null
- private var currentSmallClockLocation = IntArray(2)
- private var currentLargeClockLocation = IntArray(2)
+ private val mLayoutChangedListener =
+ object : View.OnLayoutChangeListener {
+ private var currentSmallClockView: View? = null
+ private var currentLargeClockView: View? = null
+ private var currentSmallClockLocation = IntArray(2)
+ private var currentLargeClockLocation = IntArray(2)
- override fun onLayoutChange(
- view: View?,
- left: Int,
- top: Int,
- right: Int,
- bottom: Int,
- oldLeft: Int,
- oldTop: Int,
- oldRight: Int,
- oldBottom: Int
- ) {
- val parent = (view?.parent) as FrameLayout
+ override fun onLayoutChange(
+ view: View?,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int,
+ oldLeft: Int,
+ oldTop: Int,
+ oldRight: Int,
+ oldBottom: Int
+ ) {
+ val parent = (view?.parent) as FrameLayout
- // don't pass in negative bounds when clocks are in transition state
- if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
- return
- }
+ // don't pass in negative bounds when clocks are in transition state
+ if (view.locationOnScreen[0] < 0 || view.locationOnScreen[1] < 0) {
+ return
+ }
- // SMALL CLOCK
- if (parent.id == R.id.lockscreen_clock_view) {
- // view bounds have changed due to clock size changing (i.e. different character widths)
- // AND/OR the view has been translated when transitioning between small and large clock
- if (view != currentSmallClockView ||
- !view.locationOnScreen.contentEquals(currentSmallClockLocation)) {
- currentSmallClockView = view
- currentSmallClockLocation = view.locationOnScreen
- updateRegionSampler(view)
+ // SMALL CLOCK
+ if (parent.id == R.id.lockscreen_clock_view) {
+ // view bounds have changed due to clock size changing (i.e. different character
+ // widths)
+ // AND/OR the view has been translated when transitioning between small and
+ // large clock
+ if (
+ view != currentSmallClockView ||
+ !view.locationOnScreen.contentEquals(currentSmallClockLocation)
+ ) {
+ currentSmallClockView = view
+ currentSmallClockLocation = view.locationOnScreen
+ updateRegionSampler(view)
+ }
+ }
+ // LARGE CLOCK
+ else if (parent.id == R.id.lockscreen_clock_view_large) {
+ if (
+ view != currentLargeClockView ||
+ !view.locationOnScreen.contentEquals(currentLargeClockLocation)
+ ) {
+ currentLargeClockView = view
+ currentLargeClockLocation = view.locationOnScreen
+ updateRegionSampler(view)
+ }
+ }
}
}
- // LARGE CLOCK
- else if (parent.id == R.id.lockscreen_clock_view_large) {
- if (view != currentLargeClockView ||
- !view.locationOnScreen.contentEquals(currentLargeClockLocation)) {
- currentLargeClockView = view
- currentLargeClockLocation = view.locationOnScreen
- updateRegionSampler(view)
- }
- }
- }
- }
private fun updateColors() {
val wallpaperManager = WallpaperManager.getInstance(context)
@@ -189,30 +198,33 @@
private fun updateRegionSampler(sampledRegion: View) {
regionSampler?.stopRegionSampler()
- regionSampler = createRegionSampler(
- sampledRegion,
- mainExecutor,
- bgExecutor,
- regionSamplingEnabled,
- ::updateColors
- )?.apply { startRegionSampler() }
+ regionSampler =
+ createRegionSampler(
+ sampledRegion,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingEnabled,
+ ::updateColors
+ )
+ ?.apply { startRegionSampler() }
updateColors()
}
protected open fun createRegionSampler(
- sampledView: View?,
- mainExecutor: Executor?,
- bgExecutor: Executor?,
- regionSamplingEnabled: Boolean,
- updateColors: () -> Unit
+ sampledView: View?,
+ mainExecutor: Executor?,
+ bgExecutor: Executor?,
+ regionSamplingEnabled: Boolean,
+ updateColors: () -> Unit
): RegionSampler? {
return RegionSampler(
sampledView,
mainExecutor,
bgExecutor,
regionSamplingEnabled,
- updateColors)
+ updateColors
+ )
}
var regionSampler: RegionSampler? = null
@@ -224,64 +236,68 @@
private var smallClockIsDark = true
private var largeClockIsDark = true
- private val configListener = object : ConfigurationController.ConfigurationListener {
- override fun onThemeChanged() {
- clock?.events?.onColorPaletteChanged(resources)
- updateColors()
- }
-
- override fun onDensityOrFontScaleChanged() {
- updateFontSizes()
- }
- }
-
- private val batteryCallback = object : BatteryStateChangeCallback {
- override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
- if (isKeyguardVisible && !isCharging && charging) {
- clock?.animations?.charge()
+ private val configListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onThemeChanged() {
+ clock?.events?.onColorPaletteChanged(resources)
+ updateColors()
}
- isCharging = charging
- }
- }
- private val localeBroadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- clock?.events?.onLocaleChanged(Locale.getDefault())
+ override fun onDensityOrFontScaleChanged() {
+ updateFontSizes()
+ }
}
- }
- private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
- override fun onKeyguardVisibilityChanged(visible: Boolean) {
- isKeyguardVisible = visible
- if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
- if (!isKeyguardVisible) {
- clock?.animations?.doze(if (isDozing) 1f else 0f)
+ private val batteryCallback =
+ object : BatteryStateChangeCallback {
+ override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
+ if (isKeyguardVisible && !isCharging && charging) {
+ clock?.animations?.charge()
+ }
+ isCharging = charging
+ }
+ }
+
+ private val localeBroadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ clock?.events?.onLocaleChanged(Locale.getDefault())
+ }
+ }
+
+ private val keyguardUpdateMonitorCallback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onKeyguardVisibilityChanged(visible: Boolean) {
+ isKeyguardVisible = visible
+ if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ if (!isKeyguardVisible) {
+ clock?.animations?.doze(if (isDozing) 1f else 0f)
+ }
+ }
+
+ smallTimeListener?.update(shouldTimeListenerRun)
+ largeTimeListener?.update(shouldTimeListenerRun)
+ }
+
+ override fun onTimeFormatChanged(timeFormat: String) {
+ clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+
+ override fun onTimeZoneChanged(timeZone: TimeZone) {
+ clock?.events?.onTimeZoneChanged(timeZone)
+ }
+
+ override fun onUserSwitchComplete(userId: Int) {
+ clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
+ }
+
+ override fun onWeatherDataChanged(data: Weather?) {
+ if (data != null) {
+ clock?.events?.onWeatherDataChanged(data)
}
}
-
- smallTimeListener?.update(shouldTimeListenerRun)
- largeTimeListener?.update(shouldTimeListenerRun)
}
- override fun onTimeFormatChanged(timeFormat: String) {
- clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- }
-
- override fun onTimeZoneChanged(timeZone: TimeZone) {
- clock?.events?.onTimeZoneChanged(timeZone)
- }
-
- override fun onUserSwitchComplete(userId: Int) {
- clock?.events?.onTimeFormatChanged(DateFormat.is24HourFormat(context))
- }
-
- override fun onWeatherDataChanged(data: Weather?) {
- if (data != null) {
- clock?.events?.onWeatherDataChanged(data)
- }
- }
- }
-
fun registerListeners(parent: View) {
if (isRegistered) {
return
@@ -295,17 +311,18 @@
configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
- disposableHandle = parent.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.STARTED) {
- listenForDozing(this)
- if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
- listenForDozeAmountTransition(this)
- listenForAnyStateToAodTransition(this)
- } else {
- listenForDozeAmount(this)
+ disposableHandle =
+ parent.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ listenForDozing(this)
+ if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
+ listenForDozeAmountTransition(this)
+ listenForAnyStateToAodTransition(this)
+ } else {
+ listenForDozeAmount(this)
+ }
}
}
- }
smallTimeListener?.update(shouldTimeListenerRun)
largeTimeListener?.update(shouldTimeListenerRun)
}
@@ -344,10 +361,18 @@
}
private fun updateFontSizes() {
- clock?.smallClock?.events?.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat())
- clock?.largeClock?.events?.onFontSettingChanged(
- resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat())
+ clock
+ ?.smallClock
+ ?.events
+ ?.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+ )
+ clock
+ ?.largeClock
+ ?.events
+ ?.onFontSettingChanged(
+ resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()
+ )
}
private fun handleDoze(doze: Float) {
@@ -359,68 +384,59 @@
@VisibleForTesting
internal fun listenForDozeAmount(scope: CoroutineScope): Job {
- return scope.launch {
- keyguardInteractor.dozeAmount.collect {
- handleDoze(it)
- }
- }
+ return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
}
@VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.dozeAmountTransition.collect {
- handleDoze(it.value)
- }
+ keyguardTransitionInteractor.dozeAmountTransition.collect { handleDoze(it.value) }
}
}
/**
- * When keyguard is displayed again after being gone, the clock must be reset to full
- * dozing.
+ * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
*/
@VisibleForTesting
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.anyStateToAodTransition.filter {
- it.transitionState == TransitionState.FINISHED
- }.collect {
- handleDoze(1f)
- }
+ keyguardTransitionInteractor.anyStateToAodTransition
+ .filter { it.transitionState == TransitionState.FINISHED }
+ .collect { handleDoze(1f) }
}
}
@VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch {
- combine (
- keyguardInteractor.dozeAmount,
- keyguardInteractor.isDozing,
- ) { localDozeAmount, localIsDozing ->
- localDozeAmount > dozeAmount || localIsDozing
- }
- .collect { localIsDozing ->
- isDozing = localIsDozing
- }
+ combine(
+ keyguardInteractor.dozeAmount,
+ keyguardInteractor.isDozing,
+ ) { localDozeAmount, localIsDozing ->
+ localDozeAmount > dozeAmount || localIsDozing
+ }
+ .collect { localIsDozing -> isDozing = localIsDozing }
}
}
class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) {
- val predrawListener = ViewTreeObserver.OnPreDrawListener {
- clockFace.events.onTimeTick()
- true
- }
-
- val secondsRunnable = object : Runnable {
- override fun run() {
- if (!isRunning) {
- return
- }
-
- executor.executeDelayed(this, 990)
+ val predrawListener =
+ ViewTreeObserver.OnPreDrawListener {
clockFace.events.onTimeTick()
+ true
}
- }
+
+ val secondsRunnable =
+ object : Runnable {
+ override fun run() {
+ if (!isRunning) {
+ return
+ }
+
+ executor.executeDelayed(this, 990)
+ clockFace.events.onTimeTick()
+ }
+ }
var isRunning: Boolean = false
private set
@@ -432,7 +448,9 @@
isRunning = true
when (clockFace.events.tickRate) {
- ClockTickRate.PER_MINUTE -> {/* Handled by KeyguardClockSwitchController */}
+ ClockTickRate.PER_MINUTE -> {
+ /* Handled by KeyguardClockSwitchController */
+ }
ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
ClockTickRate.PER_FRAME -> {
clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener)
@@ -442,7 +460,9 @@
}
fun stop() {
- if (!isRunning) { return }
+ if (!isRunning) {
+ return
+ }
isRunning = false
clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 3d39da6..7e86a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -22,6 +22,8 @@
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -31,7 +33,6 @@
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.CommandQueue.Callbacks
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay
@@ -41,7 +42,9 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onStart
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -52,6 +55,7 @@
constructor(
private val repository: KeyguardRepository,
private val commandQueue: CommandQueue,
+ featureFlags: FeatureFlags,
) {
/**
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
@@ -129,6 +133,29 @@
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ /** Keyguard is present and is not occluded. */
+ val isKeyguardVisible: Flow<Boolean> =
+ combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded }
+
+ /** Whether camera is launched over keyguard. */
+ var isSecureCameraActive =
+ if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) {
+ combine(
+ isKeyguardVisible,
+ repository.isBouncerShowing,
+ onCameraLaunchDetected,
+ ) { isKeyguardVisible, isBouncerShowing, cameraLaunchEvent ->
+ when {
+ isKeyguardVisible -> false
+ isBouncerShowing -> false
+ else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP
+ }
+ }
+ .onStart { emit(false) }
+ } else {
+ flowOf(false)
+ }
+
/** The approximate location on the screen of the fingerprint sensor, if one is available. */
val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 43a2017..f7fec80 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -58,7 +58,7 @@
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
-import java.util.*
+import java.util.TimeZone
import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever
@@ -105,7 +105,9 @@
repository = FakeKeyguardRepository()
underTest = ClockEventController(
- KeyguardInteractor(repository = repository, commandQueue = commandQueue),
+ KeyguardInteractor(repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags),
KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher,
batteryController,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index 05bd1e4..3d0d036 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -159,7 +159,9 @@
mAuthRippleController,
mResources,
new KeyguardTransitionInteractor(mTransitionRepository),
- new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue),
+ new KeyguardInteractor(new FakeKeyguardRepository(),
+ mCommandQueue,
+ mFeatureFlags),
mFeatureFlags
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index fb54d6d..4415033 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -157,25 +157,28 @@
dumpManager = mock(),
userHandle = UserHandle.SYSTEM,
)
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
+ set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
+ set(Flags.REVAMPED_WALLPAPER_UI, true)
+ set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
underTest.interactor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
KeyguardInteractor(
repository = FakeKeyguardRepository(),
commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
registry = mock(),
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true)
- set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true)
- set(Flags.REVAMPED_WALLPAPER_UI, true)
- set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true)
- },
+ featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 68d13d3..d938243 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,30 +18,34 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
+import android.content.Context
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
+import com.android.systemui.settings.DisplayTracker
import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.CommandQueue.Callbacks
-import com.android.systemui.util.mockito.argumentCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.Mockito.verify
+import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
class KeyguardInteractorTest : SysuiTestCase() {
- @Mock private lateinit var commandQueue: CommandQueue
+ private lateinit var commandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var testScope: TestScope
private lateinit var underTest: KeyguardInteractor
private lateinit var repository: FakeKeyguardRepository
@@ -49,38 +53,134 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
+ featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
+ commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
+ testScope = TestScope()
repository = FakeKeyguardRepository()
- underTest = KeyguardInteractor(repository, commandQueue)
+ underTest = KeyguardInteractor(repository, commandQueue, featureFlags)
}
@Test
- fun onCameraLaunchDetected() = runTest {
- val flow = underTest.onCameraLaunchDetected
- var cameraLaunchSource = collectLastValue(flow)
- runCurrent()
+ fun onCameraLaunchDetected() =
+ testScope.runTest {
+ val flow = underTest.onCameraLaunchDetected
+ var cameraLaunchSource = collectLastValue(flow)
+ runCurrent()
- val captor = argumentCaptor<CommandQueue.Callbacks>()
- verify(commandQueue).addCallback(captor.capture())
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
- captor.value.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+ )
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
- captor.value.onCameraLaunchGestureDetected(
- StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
- )
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
- captor.value.onCameraLaunchGestureDetected(
- StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER
- )
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
+ )
+ }
+ assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
- captor.value.onCameraLaunchGestureDetected(
- StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
- )
- assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
+ flow.onCompletion { assertThat(commandQueue.callbackCount()).isEqualTo(0) }
+ }
- flow.onCompletion { verify(commandQueue).removeCallback(captor.value) }
+ @Test
+ fun testKeyguardGuardVisibilityStopsSecureCamera() =
+ testScope.runTest {
+ val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+ runCurrent()
+
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+ )
+ }
+
+ assertThat(secureCameraActive()).isTrue()
+
+ // Keyguard is showing but occluded
+ repository.setKeyguardShowing(true)
+ repository.setKeyguardOccluded(true)
+ assertThat(secureCameraActive()).isTrue()
+
+ // Keyguard is showing and not occluded
+ repository.setKeyguardOccluded(false)
+ assertThat(secureCameraActive()).isFalse()
+ }
+
+ @Test
+ fun testBouncerShowingResetsSecureCameraState() =
+ testScope.runTest {
+ val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+ runCurrent()
+
+ commandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(
+ StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+ )
+ }
+ assertThat(secureCameraActive()).isTrue()
+
+ // Keyguard is showing and not occluded
+ repository.setKeyguardShowing(true)
+ repository.setKeyguardOccluded(true)
+ assertThat(secureCameraActive()).isTrue()
+
+ repository.setBouncerShowing(true)
+ assertThat(secureCameraActive()).isFalse()
+ }
+
+ @Test
+ fun keyguardVisibilityIsDefinedAsKeyguardShowingButNotOccluded() = runTest {
+ var isVisible = collectLastValue(underTest.isKeyguardVisible)
+ repository.setKeyguardShowing(true)
+ repository.setKeyguardOccluded(false)
+
+ assertThat(isVisible()).isTrue()
+
+ repository.setKeyguardOccluded(true)
+ assertThat(isVisible()).isFalse()
+
+ repository.setKeyguardShowing(false)
+ repository.setKeyguardOccluded(true)
+ assertThat(isVisible()).isFalse()
}
+
+ @Test
+ fun secureCameraIsNotActiveWhenNoCameraLaunchEventHasBeenFiredYet() =
+ testScope.runTest {
+ val secureCameraActive = collectLastValue(underTest.isSecureCameraActive)
+ runCurrent()
+
+ assertThat(secureCameraActive()).isFalse()
+ }
+}
+
+class FakeCommandQueue(val context: Context, val displayTracker: DisplayTracker) :
+ CommandQueue(context, displayTracker) {
+ private val callbacks = mutableListOf<Callbacks>()
+
+ override fun addCallback(callback: Callbacks) {
+ callbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: Callbacks) {
+ callbacks.remove(callback)
+ }
+
+ fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
+ callbacks.forEach { func(it) }
+ }
+
+ fun callbackCount(): Int = callbacks.size
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 43287b0..240af7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -286,12 +286,18 @@
dumpManager = mock(),
userHandle = UserHandle.SYSTEM,
)
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
KeyguardInteractor(
repository = FakeKeyguardRepository(),
- commandQueue = commandQueue
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
registry =
FakeKeyguardQuickAffordanceRegistry(
@@ -311,10 +317,7 @@
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
- },
+ featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index b75a15d..8cff0ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -150,12 +150,17 @@
featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
}
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor =
- KeyguardInteractor(repository = repository, commandQueue = commandQueue),
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags
+ ),
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 702f3763..2fd39b7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -21,6 +21,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
@@ -92,10 +94,12 @@
transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository)
+ val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -105,7 +109,8 @@
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -114,7 +119,8 @@
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -123,7 +129,8 @@
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -132,7 +139,8 @@
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -141,7 +149,8 @@
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
- keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
+ keyguardInteractor =
+ KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 4b04b7b..03a347e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -125,9 +125,18 @@
),
)
repository = FakeKeyguardRepository()
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
val keyguardInteractor =
- KeyguardInteractor(repository = repository, commandQueue = commandQueue)
+ KeyguardInteractor(
+ repository = repository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags,
+ )
whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
@@ -191,10 +200,7 @@
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
- },
+ featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f4226bc..3d75967 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -25,6 +25,8 @@
import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -105,8 +107,13 @@
}
keyguardRepository = FakeKeyguardRepository()
+ val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
val keyguardInteractor =
- KeyguardInteractor(repository = keyguardRepository, commandQueue = commandQueue)
+ KeyguardInteractor(
+ repository = keyguardRepository,
+ commandQueue = commandQueue,
+ featureFlags = featureFlags
+ )
// Needs to be run on the main thread
runBlocking(IMMEDIATE) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 9bb52be..8660d09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -117,8 +117,11 @@
SUPERVISED_USER_CREATION_APP_PACKAGE,
)
- featureFlags = FakeFeatureFlags()
- featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
userRepository = FakeUserRepository()
keyguardRepository = FakeKeyguardRepository()
telephonyRepository = FakeTelephonyRepository()
@@ -139,6 +142,7 @@
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
manager = manager,
applicationScope = testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 9a4ca56..8a35cb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -82,7 +82,6 @@
private val userRepository = FakeUserRepository()
private val keyguardRepository = FakeKeyguardRepository()
- private val featureFlags = FakeFeatureFlags()
private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
@@ -233,6 +232,11 @@
}
private fun viewModel(): StatusBarUserChipViewModel {
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
return StatusBarUserChipViewModel(
context = context,
interactor =
@@ -244,9 +248,9 @@
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
+ featureFlags = featureFlags,
),
- featureFlags =
- FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) },
+ featureFlags = featureFlags,
manager = manager,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 3d4bbdb..1337d1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -134,6 +134,11 @@
resetOrExitSessionReceiver = resetOrExitSessionReceiver,
)
+ val featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.FULL_SCREEN_USER_SWITCHER, false)
+ set(Flags.FACE_AUTH_REFACTOR, true)
+ }
underTest =
UserSwitcherViewModel.Factory(
userInteractor =
@@ -145,11 +150,9 @@
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
+ featureFlags = featureFlags
),
- featureFlags =
- FakeFeatureFlags().apply {
- set(Flags.FULL_SCREEN_USER_SWITCHER, false)
- },
+ featureFlags = featureFlags,
manager = manager,
applicationScope = testScope.backgroundScope,
telephonyInteractor =