diff options
14 files changed, 421 insertions, 82 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java index 08e9cf60a65a..2a389b6132e9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java @@ -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; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index ea84438bf4ba..61394035d731 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -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); + } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 3e171361a6f1..4ac6ac8d9cce 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -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 } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index cc99eb72da16..2cbc2bb44189 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -37,4 +37,7 @@ interface KeyguardRepositoryModule { fun deviceEntryFingerprintAuthRepository( impl: DeviceEntryFingerprintAuthRepositoryImpl ): DeviceEntryFingerprintAuthRepository + + @Binds + fun keyguardBouncerRepository(impl: KeyguardBouncerRepositoryImpl): KeyguardBouncerRepository } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt index a92540d733b5..96bf815b28d8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt @@ -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. diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt index c1731e031333..9f09d53c99f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -154,6 +154,12 @@ object KeyguardBouncerViewBinder { } launch { + viewModel.isInteractable.collect { isInteractable -> + hostViewController.setInteractable(isInteractable) + } + } + + launch { viewModel.isBouncerVisible .filter { !it } .collect { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt index 737c35d866c2..b8b3a8e5db20 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -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 diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index fd931b0a794b..b5c0f201dc3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -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, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt index 81a6bc2b25b5..d2183b317bbe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt @@ -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(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt index 969537d23111..444a2a7eada6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt @@ -45,7 +45,7 @@ class KeyguardBouncerRepositoryTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) val testCoroutineScope = TestCoroutineScope() underTest = - KeyguardBouncerRepository( + KeyguardBouncerRepositoryImpl( viewMediatorCallback, systemClock, testCoroutineScope, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt index 68fff262214a..28974f8252c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractorTest.kt @@ -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(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt index 7f48ea19c91a..c5e025285944 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -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() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt new file mode 100644 index 000000000000..ea7bc91cd2d5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorWithCoroutinesTest.kt @@ -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() + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt new file mode 100644 index 000000000000..d0383e996121 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt @@ -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 + } +} |