Merge "[DO NOT MERGE] Bouncer - Intercept touch events when not visible" into tm-qpr-dev

This commit is contained in:
Matt Pietal
2023-01-31 14:45:15 +00:00
committed by Android (Google) Code Review
14 changed files with 421 additions and 82 deletions

View File

@@ -19,6 +19,7 @@ package com.android.keyguard;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
/**
@@ -33,7 +34,7 @@ import android.widget.FrameLayout;
public class KeyguardHostView extends FrameLayout {
protected ViewMediatorCallback mViewMediatorCallback;
private boolean mIsInteractable;
public KeyguardHostView(Context context) {
this(context, null);
@@ -54,4 +55,24 @@ public class KeyguardHostView extends FrameLayout {
public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
mViewMediatorCallback = viewMediatorCallback;
}
/** Set true if the view can be interacted with */
public void setInteractable(boolean isInteractable) {
mIsInteractable = isInteractable;
}
/**
* Make sure to disallow touches while transitioning the bouncer, otherwise
* it can remain interactable even when barely visible.
*/
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return !mIsInteractable;
}
/** True to consume any events that are sent to it */
@Override
public boolean onTouchEvent(MotionEvent ev) {
return true;
}
}

View File

@@ -527,4 +527,9 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
mKeyguardSecurityContainerController.updateKeyguardPosition(x);
}
}
/** Set true if the view can be interacted with */
public void setInteractable(boolean isInteractable) {
mView.setInteractable(isInteractable);
}
}

View File

@@ -41,31 +41,96 @@ import kotlinx.coroutines.flow.map
*
* Make sure to add newly added flows to the logger.
*/
interface KeyguardBouncerRepository {
/** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
val primaryBouncerVisible: StateFlow<Boolean>
val primaryBouncerShow: StateFlow<KeyguardBouncerModel?>
val primaryBouncerShowingSoon: StateFlow<Boolean>
val primaryBouncerHide: StateFlow<Boolean>
val primaryBouncerStartingToHide: StateFlow<Boolean>
val primaryBouncerStartingDisappearAnimation: StateFlow<Runnable?>
/** Determines if we want to instantaneously show the primary bouncer instead of translating. */
val primaryBouncerScrimmed: StateFlow<Boolean>
/**
* Set how much of the notification panel is showing on the screen.
* ```
* 0f = panel fully hidden = bouncer fully showing
* 1f = panel fully showing = bouncer fully hidden
* ```
*/
val panelExpansionAmount: StateFlow<Float>
val keyguardPosition: StateFlow<Float>
val onScreenTurnedOff: StateFlow<Boolean>
val isBackButtonEnabled: StateFlow<Boolean?>
/** Determines if user is already unlocked */
val keyguardAuthenticated: StateFlow<Boolean?>
val showMessage: StateFlow<BouncerShowMessageModel?>
val resourceUpdateRequests: StateFlow<Boolean>
val bouncerPromptReason: Int
val bouncerErrorMessage: CharSequence?
val isAlternateBouncerVisible: StateFlow<Boolean>
val isAlternateBouncerUIAvailable: StateFlow<Boolean>
var lastAlternateBouncerVisibleTime: Long
fun setPrimaryScrimmed(isScrimmed: Boolean)
fun setPrimaryVisible(isVisible: Boolean)
fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?)
fun setPrimaryShowingSoon(showingSoon: Boolean)
fun setPrimaryHide(hide: Boolean)
fun setPrimaryStartingToHide(startingToHide: Boolean)
fun setPrimaryStartDisappearAnimation(runnable: Runnable?)
fun setPanelExpansion(panelExpansion: Float)
fun setKeyguardPosition(keyguardPosition: Float)
fun setResourceUpdateRequests(willUpdateResources: Boolean)
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?)
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?)
fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean)
fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean)
fun setAlternateVisible(isVisible: Boolean)
fun setAlternateBouncerUIAvailable(isAvailable: Boolean)
}
@SysUISingleton
class KeyguardBouncerRepository
class KeyguardBouncerRepositoryImpl
@Inject
constructor(
private val viewMediatorCallback: ViewMediatorCallback,
private val clock: SystemClock,
@Application private val applicationScope: CoroutineScope,
@BouncerLog private val buffer: TableLogBuffer,
) {
) : KeyguardBouncerRepository {
/** Values associated with the PrimaryBouncer (pin/pattern/password) input. */
private val _primaryBouncerVisible = MutableStateFlow(false)
val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
override val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
private val _primaryBouncerShow = MutableStateFlow<KeyguardBouncerModel?>(null)
val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
override val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
private val _primaryBouncerShowingSoon = MutableStateFlow(false)
val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
override val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
private val _primaryBouncerHide = MutableStateFlow(false)
val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
override val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
private val _primaryBouncerStartingToHide = MutableStateFlow(false)
val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
override val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
val primaryBouncerStartingDisappearAnimation = _primaryBouncerDisappearAnimation.asStateFlow()
override val primaryBouncerStartingDisappearAnimation =
_primaryBouncerDisappearAnimation.asStateFlow()
/** Determines if we want to instantaneously show the primary bouncer instead of translating. */
private val _primaryBouncerScrimmed = MutableStateFlow(false)
val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
/**
* Set how much of the notification panel is showing on the screen.
* ```
@@ -74,23 +139,23 @@ constructor(
* ```
*/
private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
private val _keyguardPosition = MutableStateFlow(0f)
val keyguardPosition = _keyguardPosition.asStateFlow()
override val keyguardPosition = _keyguardPosition.asStateFlow()
private val _onScreenTurnedOff = MutableStateFlow(false)
val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
override val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
/** Determines if user is already unlocked */
val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
override val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
val showMessage = _showMessage.asStateFlow()
override val showMessage = _showMessage.asStateFlow()
private val _resourceUpdateRequests = MutableStateFlow(false)
val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
val bouncerPromptReason: Int
override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
override val bouncerPromptReason: Int
get() = viewMediatorCallback.bouncerPromptReason
val bouncerErrorMessage: CharSequence?
override val bouncerErrorMessage: CharSequence?
get() = viewMediatorCallback.consumeCustomMessage()
init {
@@ -99,21 +164,21 @@ constructor(
/** Values associated with the AlternateBouncer */
private val _isAlternateBouncerVisible = MutableStateFlow(false)
val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
override val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = NOT_VISIBLE
private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
val isAlternateBouncerUIAvailable: StateFlow<Boolean> =
override val isAlternateBouncerUIAvailable: StateFlow<Boolean> =
_isAlternateBouncerUIAvailable.asStateFlow()
fun setPrimaryScrimmed(isScrimmed: Boolean) {
override fun setPrimaryScrimmed(isScrimmed: Boolean) {
_primaryBouncerScrimmed.value = isScrimmed
}
fun setPrimaryVisible(isVisible: Boolean) {
override fun setPrimaryVisible(isVisible: Boolean) {
_primaryBouncerVisible.value = isVisible
}
fun setAlternateVisible(isVisible: Boolean) {
override fun setAlternateVisible(isVisible: Boolean) {
if (isVisible && !_isAlternateBouncerVisible.value) {
lastAlternateBouncerVisibleTime = clock.uptimeMillis()
} else if (!isVisible) {
@@ -122,55 +187,55 @@ constructor(
_isAlternateBouncerVisible.value = isVisible
}
fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
_isAlternateBouncerUIAvailable.value = isAvailable
}
fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
override fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
_primaryBouncerShow.value = keyguardBouncerModel
}
fun setPrimaryShowingSoon(showingSoon: Boolean) {
override fun setPrimaryShowingSoon(showingSoon: Boolean) {
_primaryBouncerShowingSoon.value = showingSoon
}
fun setPrimaryHide(hide: Boolean) {
override fun setPrimaryHide(hide: Boolean) {
_primaryBouncerHide.value = hide
}
fun setPrimaryStartingToHide(startingToHide: Boolean) {
override fun setPrimaryStartingToHide(startingToHide: Boolean) {
_primaryBouncerStartingToHide.value = startingToHide
}
fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
override fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
_primaryBouncerDisappearAnimation.value = runnable
}
fun setPanelExpansion(panelExpansion: Float) {
override fun setPanelExpansion(panelExpansion: Float) {
_panelExpansionAmount.value = panelExpansion
}
fun setKeyguardPosition(keyguardPosition: Float) {
override fun setKeyguardPosition(keyguardPosition: Float) {
_keyguardPosition.value = keyguardPosition
}
fun setResourceUpdateRequests(willUpdateResources: Boolean) {
override fun setResourceUpdateRequests(willUpdateResources: Boolean) {
_resourceUpdateRequests.value = willUpdateResources
}
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
override fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
_showMessage.value = bouncerShowMessageModel
}
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
override fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
_keyguardAuthenticated.value = keyguardAuthenticated
}
fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
override fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
_isBackButtonEnabled.value = isBackButtonEnabled
}
fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
override fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
_onScreenTurnedOff.value = onScreenTurnedOff
}

View File

@@ -37,4 +37,7 @@ interface KeyguardRepositoryModule {
fun deviceEntryFingerprintAuthRepository(
impl: DeviceEntryFingerprintAuthRepositoryImpl
): DeviceEntryFingerprintAuthRepository
@Binds
fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository
}

View File

@@ -113,6 +113,8 @@ constructor(
0f
}
}
/** Allow for interaction when just about fully visible */
val isInteractable: Flow<Boolean> = bouncerExpansion.map { it > 0.9 }
// TODO(b/243685699): Move isScrimmed logic to data layer.
// TODO(b/243695312): Encapsulate all of the show logic for the bouncer.

View File

@@ -153,6 +153,12 @@ object KeyguardBouncerViewBinder {
}
}
launch {
viewModel.isInteractable.collect { isInteractable ->
hostViewController.setInteractable(isInteractable)
}
}
launch {
viewModel.isBouncerVisible
.filter { !it }

View File

@@ -39,6 +39,9 @@ constructor(
/** Observe on bouncer visibility. */
val isBouncerVisible: Flow<Boolean> = interactor.isVisible
/** Can the user interact with the view? */
val isInteractable: Flow<Boolean> = interactor.isInteractable
/** Observe whether bouncer is showing. */
val show: Flow<KeyguardBouncerModel> = interactor.show

View File

@@ -52,7 +52,6 @@ import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestableContext
@@ -61,9 +60,8 @@ import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.MODERN_ALTERNATE_BOUNCER
import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.recents.OverviewProxyService
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -111,7 +109,7 @@ class SideFpsControllerTest : SysuiTestCase() {
@Captor lateinit var overlayCaptor: ArgumentCaptor<View>
@Captor lateinit var overlayViewParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
private lateinit var keyguardBouncerRepository: FakeKeyguardBouncerRepository
private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
private val featureFlags = FakeFeatureFlags()
private val executor = FakeExecutor(FakeSystemClock())
@@ -135,13 +133,7 @@ class SideFpsControllerTest : SysuiTestCase() {
@Before
fun setup() {
featureFlags.set(MODERN_ALTERNATE_BOUNCER, true)
keyguardBouncerRepository =
KeyguardBouncerRepository(
mock(ViewMediatorCallback::class.java),
FakeSystemClock(),
TestCoroutineScope(),
mock(TableLogBuffer::class.java),
)
keyguardBouncerRepository = FakeKeyguardBouncerRepository()
alternateBouncerInteractor =
AlternateBouncerInteractor(
keyguardBouncerRepository,

View File

@@ -29,6 +29,7 @@ import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.BiometricRepository
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerCallbackInteractor
import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
@@ -65,7 +66,7 @@ class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControlle
allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
MockitoAnnotations.initMocks(this)
keyguardBouncerRepository =
KeyguardBouncerRepository(
KeyguardBouncerRepositoryImpl(
mock(com.android.keyguard.ViewMediatorCallback::class.java),
FakeSystemClock(),
TestCoroutineScope(),

View File

@@ -45,7 +45,7 @@ class KeyguardBouncerRepositoryTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
val testCoroutineScope = TestCoroutineScope()
underTest =
KeyguardBouncerRepository(
KeyguardBouncerRepositoryImpl(
viewMediatorCallback,
systemClock,
testCoroutineScope,

View File

@@ -25,6 +25,7 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeBiometricRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepositoryImpl
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.util.time.FakeSystemClock
import com.android.systemui.util.time.SystemClock
@@ -58,7 +59,7 @@ class AlternateBouncerInteractorTest : SysuiTestCase() {
fun setup() {
MockitoAnnotations.initMocks(this)
bouncerRepository =
KeyguardBouncerRepository(
KeyguardBouncerRepositoryImpl(
mock(ViewMediatorCallback::class.java),
FakeSystemClock(),
TestCoroutineScope(),

View File

@@ -68,13 +68,13 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private val mainHandler = FakeHandler(Looper.getMainLooper())
private lateinit var mPrimaryBouncerInteractor: PrimaryBouncerInteractor
private lateinit var underTest: PrimaryBouncerInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
DejankUtils.setImmediate(true)
mPrimaryBouncerInteractor =
underTest =
PrimaryBouncerInteractor(
repository,
bouncerView,
@@ -94,7 +94,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testShow_isScrimmed() {
mPrimaryBouncerInteractor.show(true)
underTest.show(true)
verify(repository).setOnScreenTurnedOff(false)
verify(repository).setKeyguardAuthenticated(null)
verify(repository).setPrimaryHide(false)
@@ -124,7 +124,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testHide() {
mPrimaryBouncerInteractor.hide()
underTest.hide()
verify(falsingCollector).onBouncerHidden()
verify(keyguardStateController).notifyBouncerShowing(false)
verify(repository).setPrimaryShowingSoon(false)
@@ -137,7 +137,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testExpansion() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
mPrimaryBouncerInteractor.setPanelExpansion(0.6f)
underTest.setPanelExpansion(0.6f)
verify(repository).setPanelExpansion(0.6f)
verify(mPrimaryBouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
}
@@ -146,7 +146,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
fun testExpansion_fullyShown() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
`when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
underTest.setPanelExpansion(EXPANSION_VISIBLE)
verify(falsingCollector).onBouncerShown()
verify(mPrimaryBouncerCallbackInteractor).dispatchFullyShown()
}
@@ -155,7 +155,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
fun testExpansion_fullyHidden() {
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
`when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
mPrimaryBouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
underTest.setPanelExpansion(EXPANSION_HIDDEN)
verify(repository).setPrimaryVisible(false)
verify(repository).setPrimaryShow(null)
verify(repository).setPrimaryHide(true)
@@ -167,7 +167,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testExpansion_startingToHide() {
`when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
mPrimaryBouncerInteractor.setPanelExpansion(0.1f)
underTest.setPanelExpansion(0.1f)
verify(repository).setPrimaryStartingToHide(true)
verify(mPrimaryBouncerCallbackInteractor).dispatchStartingToHide()
}
@@ -175,7 +175,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testShowMessage() {
val argCaptor = ArgumentCaptor.forClass(BouncerShowMessageModel::class.java)
mPrimaryBouncerInteractor.showMessage("abc", null)
underTest.showMessage("abc", null)
verify(repository).setShowMessage(argCaptor.capture())
assertThat(argCaptor.value.message).isEqualTo("abc")
}
@@ -184,62 +184,62 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
fun testDismissAction() {
val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
val cancelAction = mock(Runnable::class.java)
mPrimaryBouncerInteractor.setDismissAction(onDismissAction, cancelAction)
underTest.setDismissAction(onDismissAction, cancelAction)
verify(bouncerViewDelegate).setDismissAction(onDismissAction, cancelAction)
}
@Test
fun testUpdateResources() {
mPrimaryBouncerInteractor.updateResources()
underTest.updateResources()
verify(repository).setResourceUpdateRequests(true)
}
@Test
fun testNotifyKeyguardAuthenticated() {
mPrimaryBouncerInteractor.notifyKeyguardAuthenticated(true)
underTest.notifyKeyguardAuthenticated(true)
verify(repository).setKeyguardAuthenticated(true)
}
@Test
fun testNotifyShowedMessage() {
mPrimaryBouncerInteractor.onMessageShown()
underTest.onMessageShown()
verify(repository).setShowMessage(null)
}
@Test
fun testOnScreenTurnedOff() {
mPrimaryBouncerInteractor.onScreenTurnedOff()
underTest.onScreenTurnedOff()
verify(repository).setOnScreenTurnedOff(true)
}
@Test
fun testSetKeyguardPosition() {
mPrimaryBouncerInteractor.setKeyguardPosition(0f)
underTest.setKeyguardPosition(0f)
verify(repository).setKeyguardPosition(0f)
}
@Test
fun testNotifyKeyguardAuthenticatedHandled() {
mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedHandled()
underTest.notifyKeyguardAuthenticatedHandled()
verify(repository).setKeyguardAuthenticated(null)
}
@Test
fun testNotifyUpdatedResources() {
mPrimaryBouncerInteractor.notifyUpdatedResources()
underTest.notifyUpdatedResources()
verify(repository).setResourceUpdateRequests(false)
}
@Test
fun testSetBackButtonEnabled() {
mPrimaryBouncerInteractor.setBackButtonEnabled(true)
underTest.setBackButtonEnabled(true)
verify(repository).setIsBackButtonEnabled(true)
}
@Test
fun testStartDisappearAnimation() {
val runnable = mock(Runnable::class.java)
mPrimaryBouncerInteractor.startDisappearAnimation(runnable)
underTest.startDisappearAnimation(runnable)
verify(repository).setPrimaryStartDisappearAnimation(any(Runnable::class.java))
}
@@ -248,42 +248,42 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
`when`(repository.primaryBouncerVisible.value).thenReturn(true)
`when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
`when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isTrue()
assertThat(underTest.isFullyShowing()).isTrue()
`when`(repository.primaryBouncerVisible.value).thenReturn(false)
assertThat(mPrimaryBouncerInteractor.isFullyShowing()).isFalse()
assertThat(underTest.isFullyShowing()).isFalse()
}
@Test
fun testIsScrimmed() {
`when`(repository.primaryBouncerScrimmed.value).thenReturn(true)
assertThat(mPrimaryBouncerInteractor.isScrimmed()).isTrue()
assertThat(underTest.isScrimmed()).isTrue()
`when`(repository.primaryBouncerScrimmed.value).thenReturn(false)
assertThat(mPrimaryBouncerInteractor.isScrimmed()).isFalse()
assertThat(underTest.isScrimmed()).isFalse()
}
@Test
fun testIsInTransit() {
`when`(repository.primaryBouncerShowingSoon.value).thenReturn(true)
assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
assertThat(underTest.isInTransit()).isTrue()
`when`(repository.primaryBouncerShowingSoon.value).thenReturn(false)
assertThat(mPrimaryBouncerInteractor.isInTransit()).isFalse()
assertThat(underTest.isInTransit()).isFalse()
`when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
assertThat(mPrimaryBouncerInteractor.isInTransit()).isTrue()
assertThat(underTest.isInTransit()).isTrue()
}
@Test
fun testIsAnimatingAway() {
`when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(Runnable {})
assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isTrue()
assertThat(underTest.isAnimatingAway()).isTrue()
`when`(repository.primaryBouncerStartingDisappearAnimation.value).thenReturn(null)
assertThat(mPrimaryBouncerInteractor.isAnimatingAway()).isFalse()
assertThat(underTest.isAnimatingAway()).isFalse()
}
@Test
fun testWillDismissWithAction() {
`when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(true)
assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isTrue()
assertThat(underTest.willDismissWithAction()).isTrue()
`when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false)
assertThat(mPrimaryBouncerInteractor.willDismissWithAction()).isFalse()
assertThat(underTest.willDismissWithAction()).isFalse()
}
}

View File

@@ -0,0 +1,112 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.systemui.keyguard.domain.interactor
import android.os.Looper
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.utils.os.FakeHandler
import com.google.common.truth.Truth.assertThat
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.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
class PrimaryBouncerInteractorWithCoroutinesTest : SysuiTestCase() {
private lateinit var repository: FakeKeyguardBouncerRepository
@Mock private lateinit var bouncerView: BouncerView
@Mock private lateinit var bouncerViewDelegate: BouncerViewDelegate
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
@Mock private lateinit var primaryBouncerCallbackInteractor: PrimaryBouncerCallbackInteractor
@Mock private lateinit var falsingCollector: FalsingCollector
@Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
@Mock private lateinit var keyguardBypassController: KeyguardBypassController
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private val mainHandler = FakeHandler(Looper.getMainLooper())
private lateinit var underTest: PrimaryBouncerInteractor
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardBouncerRepository()
underTest =
PrimaryBouncerInteractor(
repository,
bouncerView,
mainHandler,
keyguardStateController,
keyguardSecurityModel,
primaryBouncerCallbackInteractor,
falsingCollector,
dismissCallbackRegistry,
keyguardBypassController,
keyguardUpdateMonitor,
)
}
@Test
fun notInteractableWhenExpansionIsBelow90Percent() = runTest {
val isInteractable = collectLastValue(underTest.isInteractable)
repository.setPrimaryVisible(true)
repository.setPanelExpansion(0.15f)
assertThat(isInteractable()).isFalse()
}
@Test
fun notInteractableWhenExpansionAbove90PercentButNotVisible() = runTest {
val isInteractable = collectLastValue(underTest.isInteractable)
repository.setPrimaryVisible(false)
repository.setPanelExpansion(0.05f)
assertThat(isInteractable()).isFalse()
}
@Test
fun isInteractableWhenExpansionAbove90PercentAndVisible() = runTest {
var isInteractable = collectLastValue(underTest.isInteractable)
repository.setPrimaryVisible(true)
repository.setPanelExpansion(0.09f)
assertThat(isInteractable()).isTrue()
repository.setPanelExpansion(0.12f)
assertThat(isInteractable()).isFalse()
repository.setPanelExpansion(0f)
assertThat(isInteractable()).isTrue()
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package com.android.systemui.keyguard.data.repository
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
/** Fake implementation of [KeyguardRepository] */
class FakeKeyguardBouncerRepository : KeyguardBouncerRepository {
private val _primaryBouncerVisible = MutableStateFlow(false)
override val primaryBouncerVisible = _primaryBouncerVisible.asStateFlow()
private val _primaryBouncerShow = MutableStateFlow<KeyguardBouncerModel?>(null)
override val primaryBouncerShow = _primaryBouncerShow.asStateFlow()
private val _primaryBouncerShowingSoon = MutableStateFlow(false)
override val primaryBouncerShowingSoon = _primaryBouncerShowingSoon.asStateFlow()
private val _primaryBouncerHide = MutableStateFlow(false)
override val primaryBouncerHide = _primaryBouncerHide.asStateFlow()
private val _primaryBouncerStartingToHide = MutableStateFlow(false)
override val primaryBouncerStartingToHide = _primaryBouncerStartingToHide.asStateFlow()
private val _primaryBouncerDisappearAnimation = MutableStateFlow<Runnable?>(null)
override val primaryBouncerStartingDisappearAnimation =
_primaryBouncerDisappearAnimation.asStateFlow()
private val _primaryBouncerScrimmed = MutableStateFlow(false)
override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow()
private val _panelExpansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
override val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
private val _keyguardPosition = MutableStateFlow(0f)
override val keyguardPosition = _keyguardPosition.asStateFlow()
private val _onScreenTurnedOff = MutableStateFlow(false)
override val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
override val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
override val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
override val showMessage = _showMessage.asStateFlow()
private val _resourceUpdateRequests = MutableStateFlow(false)
override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
override val bouncerPromptReason = 0
override val bouncerErrorMessage: CharSequence? = null
private val _isAlternateBouncerVisible = MutableStateFlow(false)
override val isAlternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = 0L
private val _isAlternateBouncerUIAvailable = MutableStateFlow<Boolean>(false)
override val isAlternateBouncerUIAvailable = _isAlternateBouncerUIAvailable.asStateFlow()
override fun setPrimaryScrimmed(isScrimmed: Boolean) {
_primaryBouncerScrimmed.value = isScrimmed
}
override fun setPrimaryVisible(isVisible: Boolean) {
_primaryBouncerVisible.value = isVisible
}
override fun setAlternateVisible(isVisible: Boolean) {
_isAlternateBouncerVisible.value = isVisible
}
override fun setAlternateBouncerUIAvailable(isAvailable: Boolean) {
_isAlternateBouncerUIAvailable.value = isAvailable
}
override fun setPrimaryShow(keyguardBouncerModel: KeyguardBouncerModel?) {
_primaryBouncerShow.value = keyguardBouncerModel
}
override fun setPrimaryShowingSoon(showingSoon: Boolean) {
_primaryBouncerShowingSoon.value = showingSoon
}
override fun setPrimaryHide(hide: Boolean) {
_primaryBouncerHide.value = hide
}
override fun setPrimaryStartingToHide(startingToHide: Boolean) {
_primaryBouncerStartingToHide.value = startingToHide
}
override fun setPrimaryStartDisappearAnimation(runnable: Runnable?) {
_primaryBouncerDisappearAnimation.value = runnable
}
override fun setPanelExpansion(panelExpansion: Float) {
_panelExpansionAmount.value = panelExpansion
}
override fun setKeyguardPosition(keyguardPosition: Float) {
_keyguardPosition.value = keyguardPosition
}
override fun setResourceUpdateRequests(willUpdateResources: Boolean) {
_resourceUpdateRequests.value = willUpdateResources
}
override fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
_showMessage.value = bouncerShowMessageModel
}
override fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
_keyguardAuthenticated.value = keyguardAuthenticated
}
override fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
_isBackButtonEnabled.value = isBackButtonEnabled
}
override fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
_onScreenTurnedOff.value = onScreenTurnedOff
}
}