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.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.KeyguardInteractor
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
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 @@ open class ClockEventController @Inject constructor(
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 @@ open class ClockEventController @Inject constructor(
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 @@ open class ClockEventController @Inject constructor(
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 @@ open class ClockEventController @Inject constructor(
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 @@ open class ClockEventController @Inject constructor(
}
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 @@ open class ClockEventController @Inject constructor(
@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 @@ open class ClockEventController @Inject constructor(
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 @@ open class ClockEventController @Inject constructor(
}
fun stop() {
if (!isRunning) { return }
if (!isRunning) {
return
}
isRunning = false
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.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.DozeTransitionModel
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.distinctUntilChanged
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 @@ class KeyguardInteractor
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 @@ constructor(
*/
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

View File

@@ -58,7 +58,7 @@ import org.mockito.Mockito.never
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 @@ class ClockEventControllerTest : SysuiTestCase() {
repository = FakeKeyguardRepository()
underTest = ClockEventController(
KeyguardInteractor(repository = repository, commandQueue = commandQueue),
KeyguardInteractor(repository = repository,
commandQueue = commandQueue,
featureFlags = featureFlags),
KeyguardTransitionInteractor(repository = transitionRepository),
broadcastDispatcher,
batteryController,

View File

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

View File

@@ -157,25 +157,28 @@ class CustomizationProviderTest : SysuiTestCase() {
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,
)

View File

@@ -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 @@ class KeyguardInteractorTest : SysuiTestCase() {
@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
}

View File

@@ -286,12 +286,18 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
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 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
)

View File

@@ -150,12 +150,17 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() {
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(

View File

@@ -21,6 +21,8 @@ import androidx.test.filters.FlakyTest
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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
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 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDreamingTransitionInteractor =
FromDreamingTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -114,7 +119,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromAodTransitionInteractor =
FromAodTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -123,7 +129,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromGoneTransitionInteractor =
FromGoneTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -132,7 +139,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromDozingTransitionInteractor =
FromDozingTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)
@@ -141,7 +149,8 @@ class KeyguardTransitionScenariosTest : SysuiTestCase() {
fromOccludedTransitionInteractor =
FromOccludedTransitionInteractor(
scope = testScope,
keyguardInteractor = KeyguardInteractor(keyguardRepository, commandQueue),
keyguardInteractor =
KeyguardInteractor(keyguardRepository, commandQueue, featureFlags),
keyguardTransitionRepository = mockTransitionRepository,
keyguardTransitionInteractor = KeyguardTransitionInteractor(transitionRepository),
)

View File

@@ -125,9 +125,18 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
),
)
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 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
keyguardStateController = keyguardStateController,
userTracker = userTracker,
activityStarter = activityStarter,
featureFlags =
FakeFeatureFlags().apply {
set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false)
},
featureFlags = featureFlags,
repository = { quickAffordanceRepository },
launchAnimator = launchAnimator,
),

View File

@@ -25,6 +25,8 @@ import android.view.ViewTreeObserver
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 @@ class FoldAodAnimationControllerTest : SysuiTestCase() {
}
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) {

View File

@@ -117,8 +117,11 @@ class UserInteractorTest : SysuiTestCase() {
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 @@ class UserInteractorTest : SysuiTestCase() {
KeyguardInteractor(
repository = keyguardRepository,
commandQueue = commandQueue,
featureFlags = featureFlags,
),
manager = manager,
applicationScope = testScope.backgroundScope,

View File

@@ -82,7 +82,6 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
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 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
}
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 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
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 =

View File

@@ -134,6 +134,11 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
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 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
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 =