[DO NOT MERGE] Bouncer - Intercept touch events when not visible am: 96cdb8a262
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20814268 Change-Id: I5b8dc2826831528bbccf8a545146f42789880d2c Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -37,4 +37,7 @@ interface KeyguardRepositoryModule {
|
||||
fun deviceEntryFingerprintAuthRepository(
|
||||
impl: DeviceEntryFingerprintAuthRepositoryImpl
|
||||
): DeviceEntryFingerprintAuthRepository
|
||||
|
||||
@Binds
|
||||
fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -153,6 +153,12 @@ object KeyguardBouncerViewBinder {
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.isInteractable.collect { isInteractable ->
|
||||
hostViewController.setInteractable(isInteractable)
|
||||
}
|
||||
}
|
||||
|
||||
launch {
|
||||
viewModel.isBouncerVisible
|
||||
.filter { !it }
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -45,7 +45,7 @@ class KeyguardBouncerRepositoryTest : SysuiTestCase() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
val testCoroutineScope = TestCoroutineScope()
|
||||
underTest =
|
||||
KeyguardBouncerRepository(
|
||||
KeyguardBouncerRepositoryImpl(
|
||||
viewMediatorCallback,
|
||||
systemClock,
|
||||
testCoroutineScope,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user