Merge "Provide API for accessing the current state of secure camera." into tm-qpr-dev

This commit is contained in:
Chandru S
2023-02-08 02:09:20 +00:00
committed by Android (Google) Code Review
14 changed files with 424 additions and 229 deletions

View File

@@ -24,8 +24,8 @@ import android.content.res.Resources
import android.text.format.DateFormat import android.text.format.DateFormat
import android.util.TypedValue import android.util.TypedValue
import android.view.View import android.view.View
import android.widget.FrameLayout
import android.view.ViewTreeObserver import android.view.ViewTreeObserver
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting import androidx.annotation.VisibleForTesting
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
@@ -40,8 +40,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached 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.KeyguardLargeClockLog
import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.ClockController import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockFaceController import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockTickRate import com.android.systemui.plugins.ClockTickRate
@@ -53,22 +53,24 @@ import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor 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.CoroutineScope
import kotlinx.coroutines.DisposableHandle import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch 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 * Controller for a Clock provided by the registry and used on the keyguard. Instantiated by
* [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController]. * [KeyguardClockSwitchController]. Functionality is forked from [AnimatableClockController].
*/ */
open class ClockEventController @Inject constructor( open class ClockEventController
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor, private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor, private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
private val broadcastDispatcher: BroadcastDispatcher, private val broadcastDispatcher: BroadcastDispatcher,
@@ -115,7 +117,8 @@ open class ClockEventController @Inject constructor(
private var disposableHandle: DisposableHandle? = null private var disposableHandle: DisposableHandle? = null
private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING) private val regionSamplingEnabled = featureFlags.isEnabled(REGION_SAMPLING)
private val mLayoutChangedListener = object : View.OnLayoutChangeListener { private val mLayoutChangedListener =
object : View.OnLayoutChangeListener {
private var currentSmallClockView: View? = null private var currentSmallClockView: View? = null
private var currentLargeClockView: View? = null private var currentLargeClockView: View? = null
private var currentSmallClockLocation = IntArray(2) private var currentSmallClockLocation = IntArray(2)
@@ -141,10 +144,14 @@ open class ClockEventController @Inject constructor(
// SMALL CLOCK // SMALL CLOCK
if (parent.id == R.id.lockscreen_clock_view) { if (parent.id == R.id.lockscreen_clock_view) {
// view bounds have changed due to clock size changing (i.e. different character widths) // view bounds have changed due to clock size changing (i.e. different character
// AND/OR the view has been translated when transitioning between small and large clock // widths)
if (view != currentSmallClockView || // AND/OR the view has been translated when transitioning between small and
!view.locationOnScreen.contentEquals(currentSmallClockLocation)) { // large clock
if (
view != currentSmallClockView ||
!view.locationOnScreen.contentEquals(currentSmallClockLocation)
) {
currentSmallClockView = view currentSmallClockView = view
currentSmallClockLocation = view.locationOnScreen currentSmallClockLocation = view.locationOnScreen
updateRegionSampler(view) updateRegionSampler(view)
@@ -152,8 +159,10 @@ open class ClockEventController @Inject constructor(
} }
// LARGE CLOCK // LARGE CLOCK
else if (parent.id == R.id.lockscreen_clock_view_large) { else if (parent.id == R.id.lockscreen_clock_view_large) {
if (view != currentLargeClockView || if (
!view.locationOnScreen.contentEquals(currentLargeClockLocation)) { view != currentLargeClockView ||
!view.locationOnScreen.contentEquals(currentLargeClockLocation)
) {
currentLargeClockView = view currentLargeClockView = view
currentLargeClockLocation = view.locationOnScreen currentLargeClockLocation = view.locationOnScreen
updateRegionSampler(view) updateRegionSampler(view)
@@ -189,13 +198,15 @@ open class ClockEventController @Inject constructor(
private fun updateRegionSampler(sampledRegion: View) { private fun updateRegionSampler(sampledRegion: View) {
regionSampler?.stopRegionSampler() regionSampler?.stopRegionSampler()
regionSampler = createRegionSampler( regionSampler =
createRegionSampler(
sampledRegion, sampledRegion,
mainExecutor, mainExecutor,
bgExecutor, bgExecutor,
regionSamplingEnabled, regionSamplingEnabled,
::updateColors ::updateColors
)?.apply { startRegionSampler() } )
?.apply { startRegionSampler() }
updateColors() updateColors()
} }
@@ -212,7 +223,8 @@ open class ClockEventController @Inject constructor(
mainExecutor, mainExecutor,
bgExecutor, bgExecutor,
regionSamplingEnabled, regionSamplingEnabled,
updateColors) updateColors
)
} }
var regionSampler: RegionSampler? = null var regionSampler: RegionSampler? = null
@@ -224,7 +236,8 @@ open class ClockEventController @Inject constructor(
private var smallClockIsDark = true private var smallClockIsDark = true
private var largeClockIsDark = true private var largeClockIsDark = true
private val configListener = object : ConfigurationController.ConfigurationListener { private val configListener =
object : ConfigurationController.ConfigurationListener {
override fun onThemeChanged() { override fun onThemeChanged() {
clock?.events?.onColorPaletteChanged(resources) clock?.events?.onColorPaletteChanged(resources)
updateColors() updateColors()
@@ -235,7 +248,8 @@ open class ClockEventController @Inject constructor(
} }
} }
private val batteryCallback = object : BatteryStateChangeCallback { private val batteryCallback =
object : BatteryStateChangeCallback {
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) { override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
if (isKeyguardVisible && !isCharging && charging) { if (isKeyguardVisible && !isCharging && charging) {
clock?.animations?.charge() clock?.animations?.charge()
@@ -244,13 +258,15 @@ open class ClockEventController @Inject constructor(
} }
} }
private val localeBroadcastReceiver = object : BroadcastReceiver() { private val localeBroadcastReceiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
clock?.events?.onLocaleChanged(Locale.getDefault()) clock?.events?.onLocaleChanged(Locale.getDefault())
} }
} }
private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { private val keyguardUpdateMonitorCallback =
object : KeyguardUpdateMonitorCallback() {
override fun onKeyguardVisibilityChanged(visible: Boolean) { override fun onKeyguardVisibilityChanged(visible: Boolean) {
isKeyguardVisible = visible isKeyguardVisible = visible
if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) { if (!featureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -295,7 +311,8 @@ open class ClockEventController @Inject constructor(
configurationController.addCallback(configListener) configurationController.addCallback(configListener)
batteryController.addCallback(batteryCallback) batteryController.addCallback(batteryCallback)
keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
disposableHandle = parent.repeatWhenAttached { disposableHandle =
parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForDozing(this) listenForDozing(this)
if (featureFlags.isEnabled(DOZING_MIGRATION_1)) { if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
@@ -344,10 +361,18 @@ open class ClockEventController @Inject constructor(
} }
private fun updateFontSizes() { private fun updateFontSizes() {
clock?.smallClock?.events?.onFontSettingChanged( clock
resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()) ?.smallClock
clock?.largeClock?.events?.onFontSettingChanged( ?.events
resources.getDimensionPixelSize(R.dimen.large_clock_text_size).toFloat()) ?.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) { private fun handleDoze(doze: Float) {
@@ -359,59 +384,50 @@ open class ClockEventController @Inject constructor(
@VisibleForTesting @VisibleForTesting
internal fun listenForDozeAmount(scope: CoroutineScope): Job { internal fun listenForDozeAmount(scope: CoroutineScope): Job {
return scope.launch { return scope.launch { keyguardInteractor.dozeAmount.collect { handleDoze(it) } }
keyguardInteractor.dozeAmount.collect {
handleDoze(it)
}
}
} }
@VisibleForTesting @VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job { internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch { return scope.launch {
keyguardTransitionInteractor.dozeAmountTransition.collect { keyguardTransitionInteractor.dozeAmountTransition.collect { handleDoze(it.value) }
handleDoze(it.value)
}
} }
} }
/** /**
* When keyguard is displayed again after being gone, the clock must be reset to full * When keyguard is displayed again after being gone, the clock must be reset to full dozing.
* dozing.
*/ */
@VisibleForTesting @VisibleForTesting
internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job { internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
return scope.launch { return scope.launch {
keyguardTransitionInteractor.anyStateToAodTransition.filter { keyguardTransitionInteractor.anyStateToAodTransition
it.transitionState == TransitionState.FINISHED .filter { it.transitionState == TransitionState.FINISHED }
}.collect { .collect { handleDoze(1f) }
handleDoze(1f)
}
} }
} }
@VisibleForTesting @VisibleForTesting
internal fun listenForDozing(scope: CoroutineScope): Job { internal fun listenForDozing(scope: CoroutineScope): Job {
return scope.launch { return scope.launch {
combine ( combine(
keyguardInteractor.dozeAmount, keyguardInteractor.dozeAmount,
keyguardInteractor.isDozing, keyguardInteractor.isDozing,
) { localDozeAmount, localIsDozing -> ) { localDozeAmount, localIsDozing ->
localDozeAmount > dozeAmount || localIsDozing localDozeAmount > dozeAmount || localIsDozing
} }
.collect { localIsDozing -> .collect { localIsDozing -> isDozing = localIsDozing }
isDozing = localIsDozing
}
} }
} }
class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) { class TimeListener(val clockFace: ClockFaceController, val executor: DelayableExecutor) {
val predrawListener = ViewTreeObserver.OnPreDrawListener { val predrawListener =
ViewTreeObserver.OnPreDrawListener {
clockFace.events.onTimeTick() clockFace.events.onTimeTick()
true true
} }
val secondsRunnable = object : Runnable { val secondsRunnable =
object : Runnable {
override fun run() { override fun run() {
if (!isRunning) { if (!isRunning) {
return return
@@ -432,7 +448,9 @@ open class ClockEventController @Inject constructor(
isRunning = true isRunning = true
when (clockFace.events.tickRate) { when (clockFace.events.tickRate) {
ClockTickRate.PER_MINUTE -> {/* Handled by KeyguardClockSwitchController */} ClockTickRate.PER_MINUTE -> {
/* Handled by KeyguardClockSwitchController */
}
ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable) ClockTickRate.PER_SECOND -> executor.execute(secondsRunnable)
ClockTickRate.PER_FRAME -> { ClockTickRate.PER_FRAME -> {
clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener) clockFace.view.viewTreeObserver.addOnPreDrawListener(predrawListener)
@@ -442,7 +460,9 @@ open class ClockEventController @Inject constructor(
} }
fun stop() { fun stop() {
if (!isRunning) { return } if (!isRunning) {
return
}
isRunning = false isRunning = false
clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener) clockFace.view.viewTreeObserver.removeOnPreDrawListener(predrawListener)

View File

@@ -22,6 +22,8 @@ import android.graphics.Point
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton 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.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
@@ -31,7 +33,6 @@ import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.CommandQueue.Callbacks
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@@ -41,7 +42,9 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.merge 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. * Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -52,6 +55,7 @@ class KeyguardInteractor
constructor( constructor(
private val repository: KeyguardRepository, private val repository: KeyguardRepository,
private val commandQueue: CommandQueue, 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 * 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 @@ constructor(
*/ */
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState 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. */ /** The approximate location on the screen of the fingerprint sensor, if one is available. */
val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation

View File

@@ -58,7 +58,7 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.times import org.mockito.Mockito.times
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoJUnit
import java.util.* import java.util.TimeZone
import java.util.concurrent.Executor import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
@@ -105,7 +105,9 @@ class ClockEventControllerTest : SysuiTestCase() {
repository = FakeKeyguardRepository() repository = FakeKeyguardRepository()
underTest = ClockEventController( underTest = ClockEventController(
KeyguardInteractor(repository = repository, commandQueue = commandQueue), KeyguardInteractor(repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags),
KeyguardTransitionInteractor(repository = transitionRepository), KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher, broadcastDispatcher,
batteryController, batteryController,

View File

@@ -159,7 +159,9 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase {
mAuthRippleController, mAuthRippleController,
mResources, mResources,
new KeyguardTransitionInteractor(mTransitionRepository), new KeyguardTransitionInteractor(mTransitionRepository),
new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue), new KeyguardInteractor(new FakeKeyguardRepository(),
mCommandQueue,
mFeatureFlags),
mFeatureFlags mFeatureFlags
); );
} }

View File

@@ -157,25 +157,28 @@ class CustomizationProviderTest : SysuiTestCase() {
dumpManager = mock(), dumpManager = mock(),
userHandle = UserHandle.SYSTEM, 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 = underTest.interactor =
KeyguardQuickAffordanceInteractor( KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor =
KeyguardInteractor( KeyguardInteractor(
repository = FakeKeyguardRepository(), repository = FakeKeyguardRepository(),
commandQueue = commandQueue, commandQueue = commandQueue,
featureFlags = featureFlags,
), ),
registry = mock(), registry = mock(),
lockPatternUtils = lockPatternUtils, lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController, keyguardStateController = keyguardStateController,
userTracker = userTracker, userTracker = userTracker,
activityStarter = activityStarter, activityStarter = activityStarter,
featureFlags = featureFlags = 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)
},
repository = { quickAffordanceRepository }, repository = { quickAffordanceRepository },
launchAnimator = launchAnimator, launchAnimator = launchAnimator,
) )

View File

@@ -18,30 +18,34 @@
package com.android.systemui.keyguard.domain.interactor package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager import android.app.StatusBarManager
import android.content.Context
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue 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.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel 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
import com.android.systemui.statusbar.CommandQueue.Callbacks
import com.android.systemui.util.mockito.argumentCaptor
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.onCompletion import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.junit.runners.JUnit4 import org.junit.runners.JUnit4
import org.mockito.Mock import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@RunWith(JUnit4::class) @RunWith(JUnit4::class)
class KeyguardInteractorTest : SysuiTestCase() { 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 underTest: KeyguardInteractor
private lateinit var repository: FakeKeyguardRepository private lateinit var repository: FakeKeyguardRepository
@@ -49,38 +53,134 @@ class KeyguardInteractorTest : SysuiTestCase() {
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) 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() repository = FakeKeyguardRepository()
underTest = KeyguardInteractor(repository, commandQueue) underTest = KeyguardInteractor(repository, commandQueue, featureFlags)
} }
@Test @Test
fun onCameraLaunchDetected() = runTest { fun onCameraLaunchDetected() =
testScope.runTest {
val flow = underTest.onCameraLaunchDetected val flow = underTest.onCameraLaunchDetected
var cameraLaunchSource = collectLastValue(flow) var cameraLaunchSource = collectLastValue(flow)
runCurrent() runCurrent()
val captor = argumentCaptor<CommandQueue.Callbacks>() commandQueue.doForEachCallback {
verify(commandQueue).addCallback(captor.capture()) it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
}
captor.value.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE)
assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE) assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.WIGGLE)
captor.value.onCameraLaunchGestureDetected( commandQueue.doForEachCallback {
it.onCameraLaunchGestureDetected(
StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
) )
}
assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP) assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.POWER_DOUBLE_TAP)
captor.value.onCameraLaunchGestureDetected( commandQueue.doForEachCallback {
StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER it.onCameraLaunchGestureDetected(StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER)
) }
assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER) assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.LIFT_TRIGGER)
captor.value.onCameraLaunchGestureDetected( commandQueue.doForEachCallback {
it.onCameraLaunchGestureDetected(
StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE
) )
}
assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE) assertThat(cameraLaunchSource()).isEqualTo(CameraLaunchSourceModel.QUICK_AFFORDANCE)
flow.onCompletion { verify(commandQueue).removeCallback(captor.value) } flow.onCompletion { assertThat(commandQueue.callbackCount()).isEqualTo(0) }
}
@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
}

View File

@@ -286,12 +286,18 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
dumpManager = mock(), dumpManager = mock(),
userHandle = UserHandle.SYSTEM, userHandle = UserHandle.SYSTEM,
) )
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
underTest = underTest =
KeyguardQuickAffordanceInteractor( KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor =
KeyguardInteractor( KeyguardInteractor(
repository = FakeKeyguardRepository(), repository = FakeKeyguardRepository(),
commandQueue = commandQueue commandQueue = commandQueue,
featureFlags = featureFlags,
), ),
registry = registry =
FakeKeyguardQuickAffordanceRegistry( FakeKeyguardQuickAffordanceRegistry(
@@ -311,10 +317,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
keyguardStateController = keyguardStateController, keyguardStateController = keyguardStateController,
userTracker = userTracker, userTracker = userTracker,
activityStarter = activityStarter, activityStarter = activityStarter,
featureFlags = featureFlags = featureFlags,
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
repository = { quickAffordanceRepository }, repository = { quickAffordanceRepository },
launchAnimator = launchAnimator, launchAnimator = launchAnimator,
) )

View File

@@ -150,12 +150,17 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
featureFlags = featureFlags =
FakeFeatureFlags().apply { FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false) set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
set(Flags.FACE_AUTH_REFACTOR, true)
} }
underTest = underTest =
KeyguardQuickAffordanceInteractor( KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor =
KeyguardInteractor(repository = repository, commandQueue = commandQueue), KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags
),
registry = registry =
FakeKeyguardQuickAffordanceRegistry( FakeKeyguardQuickAffordanceRegistry(
mapOf( mapOf(

View File

@@ -21,6 +21,8 @@ import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.Interpolators 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.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepositoryImpl
@@ -92,10 +94,12 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
transitionRepository = KeyguardTransitionRepositoryImpl() transitionRepository = KeyguardTransitionRepositoryImpl()
runner = KeyguardTransitionRunner(transitionRepository) runner = KeyguardTransitionRunner(transitionRepository)
val featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
fromLockscreenTransitionInteractor = fromLockscreenTransitionInteractor =
FromLockscreenTransitionInteractor( FromLockscreenTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
shadeRepository = shadeRepository, shadeRepository = shadeRepository,
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
@@ -105,7 +109,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDreamingTransitionInteractor = fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor( FromDreamingTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
) )
@@ -114,7 +119,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromAodTransitionInteractor = fromAodTransitionInteractor =
FromAodTransitionInteractor( FromAodTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
) )
@@ -123,7 +129,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromGoneTransitionInteractor = fromGoneTransitionInteractor =
FromGoneTransitionInteractor( FromGoneTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
) )
@@ -132,7 +139,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDozingTransitionInteractor = fromDozingTransitionInteractor =
FromDozingTransitionInteractor( FromDozingTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
) )
@@ -141,7 +149,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromOccludedTransitionInteractor = fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor( FromOccludedTransitionInteractor(
scope = testScope, scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue), keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository, keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository), keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
) )

View File

@@ -125,9 +125,18 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
), ),
) )
repository = FakeKeyguardRepository() repository = FakeKeyguardRepository()
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
val keyguardInteractor = val keyguardInteractor =
KeyguardInteractor(repository = repository, commandQueue = commandQueue) KeyguardInteractor(
repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags,
)
whenever(userTracker.userHandle).thenReturn(mock()) whenever(userTracker.userHandle).thenReturn(mock())
whenever(lockPatternUtils.getStrongAuthForUser(anyInt())) whenever(lockPatternUtils.getStrongAuthForUser(anyInt()))
.thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED) .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED)
@@ -191,10 +200,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
keyguardStateController = keyguardStateController, keyguardStateController = keyguardStateController,
userTracker = userTracker, userTracker = userTracker,
activityStarter = activityStarter, activityStarter = activityStarter,
featureFlags = featureFlags = featureFlags,
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
repository = { quickAffordanceRepository }, repository = { quickAffordanceRepository },
launchAnimator = launchAnimator, launchAnimator = launchAnimator,
), ),

View File

@@ -25,6 +25,8 @@ import android.view.ViewTreeObserver
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.util.LatencyTracker import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase 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.WakefulnessLifecycle
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -105,8 +107,13 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
} }
keyguardRepository = FakeKeyguardRepository() keyguardRepository = FakeKeyguardRepository()
val featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
val keyguardInteractor = val keyguardInteractor =
KeyguardInteractor(repository = keyguardRepository, commandQueue = commandQueue) KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
featureFlags = featureFlags
)
// Needs to be run on the main thread // Needs to be run on the main thread
runBlocking(IMMEDIATE) { runBlocking(IMMEDIATE) {

View File

@@ -117,8 +117,11 @@ class UserInteractorTest : SysuiTestCase() {
SUPERVISED_USER_CREATION_APP_PACKAGE, SUPERVISED_USER_CREATION_APP_PACKAGE,
) )
featureFlags = FakeFeatureFlags() featureFlags =
featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, false) FakeFeatureFlags().apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
userRepository = FakeUserRepository() userRepository = FakeUserRepository()
keyguardRepository = FakeKeyguardRepository() keyguardRepository = FakeKeyguardRepository()
telephonyRepository = FakeTelephonyRepository() telephonyRepository = FakeTelephonyRepository()
@@ -139,6 +142,7 @@ class UserInteractorTest : SysuiTestCase() {
KeyguardInteractor( KeyguardInteractor(
repository = keyguardRepository, repository = keyguardRepository,
commandQueue = commandQueue, commandQueue = commandQueue,
featureFlags = featureFlags,
), ),
manager = manager, manager = manager,
applicationScope = testScope.backgroundScope, applicationScope = testScope.backgroundScope,

View File

@@ -82,7 +82,6 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
private val userRepository = FakeUserRepository() private val userRepository = FakeUserRepository()
private val keyguardRepository = FakeKeyguardRepository() private val keyguardRepository = FakeKeyguardRepository()
private val featureFlags = FakeFeatureFlags()
private lateinit var guestUserInteractor: GuestUserInteractor private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler private lateinit var refreshUsersScheduler: RefreshUsersScheduler
@@ -233,6 +232,11 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
} }
private fun viewModel(): StatusBarUserChipViewModel { private fun viewModel(): StatusBarUserChipViewModel {
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
return StatusBarUserChipViewModel( return StatusBarUserChipViewModel(
context = context, context = context,
interactor = interactor =
@@ -244,9 +248,9 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
KeyguardInteractor( KeyguardInteractor(
repository = keyguardRepository, repository = keyguardRepository,
commandQueue = commandQueue, commandQueue = commandQueue,
featureFlags = featureFlags,
), ),
featureFlags = featureFlags = featureFlags,
FakeFeatureFlags().apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) },
manager = manager, manager = manager,
applicationScope = testScope.backgroundScope, applicationScope = testScope.backgroundScope,
telephonyInteractor = telephonyInteractor =

View File

@@ -134,6 +134,11 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
resetOrExitSessionReceiver = resetOrExitSessionReceiver, resetOrExitSessionReceiver = resetOrExitSessionReceiver,
) )
val featureFlags =
FakeFeatureFlags().apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
set(Flags.FACE_AUTH_REFACTOR, true)
}
underTest = underTest =
UserSwitcherViewModel.Factory( UserSwitcherViewModel.Factory(
userInteractor = userInteractor =
@@ -145,11 +150,9 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
KeyguardInteractor( KeyguardInteractor(
repository = keyguardRepository, repository = keyguardRepository,
commandQueue = commandQueue, commandQueue = commandQueue,
featureFlags = featureFlags
), ),
featureFlags = featureFlags = featureFlags,
FakeFeatureFlags().apply {
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
},
manager = manager, manager = manager,
applicationScope = testScope.backgroundScope, applicationScope = testScope.backgroundScope,
telephonyInteractor = telephonyInteractor =