From 99404d72b13ff23fccdc7f1bf0cdd22786b254bb Mon Sep 17 00:00:00 2001 From: Alejandro Nijamkin Date: Mon, 1 Aug 2022 12:50:45 -0700 Subject: [PATCH] Fixes quick affordance visibility bug. In the new implementation of the KeyguardBottomAreaView, when the user locks their device, moving directly from the unlocked state into AOD/doze, we erroneously show the home controls quick affordance button for a moment/frame before hiding it. This was caused by our reliance on isDozing and dozeAmount where, what we should really look at is isDozing and isKeyguardShowing - like the CL is doing. The approach taken here is actually refactored out of the wallet quick affordance config implementation and applied generically to all quick affordance config implementation at the use-case layer. The reason I thought this could work is because I was only seeing the home controls button exhibit this bug while the wallet one was not. I didn't test the QR Code scanner one, but I bet it would have worked just like the home controls one. Bug: 240969525 Fix: 240969525 Test: locking from the unlocked home-screen proves that the fix works because it no longer flickers the quick affordance view over the AOD screen. Also, unit tests were added/modified to capture this. Change-Id: Id2917989991802825d1024d5a0d9161d4452265f --- ...odeScannerKeyguardQuickAffordanceConfig.kt | 5 - ...cessWalletKeyguardQuickAffordanceConfig.kt | 94 ++++++++----------- .../data/repository/KeyguardRepository.kt | 34 +++++++ .../repository/KeyguardRepositoryModule.kt | 3 +- .../ObserveIsKeyguardShowingUseCase.kt | 39 ++++++++ .../ObserveKeyguardQuickAffordanceUseCase.kt | 8 +- .../policy/KeyguardStateControllerExt.kt | 46 --------- .../FakeKeyguardQuickAffordanceRepository.kt | 2 +- .../data/repository/FakeKeyguardRepository.kt | 25 +++-- .../repository/KeyguardRepositoryImplTest.kt | 27 +++++- ...cannerKeyguardQuickAffordanceConfigTest.kt | 2 +- ...WalletKeyguardQuickAffordanceConfigTest.kt | 45 ++------- ...serveKeyguardQuickAffordanceUseCaseTest.kt | 39 ++++---- .../KeyguardBottomAreaViewModelTest.kt | 88 ++++++++++++----- 14 files changed, 245 insertions(+), 212 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsKeyguardShowingUseCase.kt delete mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerExt.kt diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt index 758e4114c77eb..ea6497ebcf3a9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QrCodeScannerKeyguardQuickAffordanceConfig.kt @@ -17,14 +17,12 @@ package com.android.systemui.keyguard.data.quickaffordance -import android.content.Context import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.containeddrawable.ContainedDrawable import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.qrcodescanner.controller.QRCodeScannerController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose @@ -35,12 +33,9 @@ import kotlinx.coroutines.flow.Flow class QrCodeScannerKeyguardQuickAffordanceConfig @Inject constructor( - @Application context: Context, private val controller: QRCodeScannerController, ) : KeyguardQuickAffordanceConfig { - private val appContext = context.applicationContext - override val state: Flow = conflatedCallbackFlow { val callback = object : QRCodeScannerController.Callback { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt index c686e27adb2ac..cc5a9972af933 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/QuickAccessWalletKeyguardQuickAffordanceConfig.kt @@ -29,32 +29,59 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.containeddrawable.ContainedDrawable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.policy.KeyguardStateController -import com.android.systemui.statusbar.policy.KeyguardStateControllerExt.isKeyguardShowing import com.android.systemui.wallet.controller.QuickAccessWalletController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf /** Quick access wallet quick affordance data source. */ @SysUISingleton class QuickAccessWalletKeyguardQuickAffordanceConfig @Inject constructor( - private val keyguardStateController: KeyguardStateController, private val walletController: QuickAccessWalletController, private val activityStarter: ActivityStarter, ) : KeyguardQuickAffordanceConfig { - override val state: Flow = - keyguardStateController - .isKeyguardShowing(TAG) - .flatMapLatest { isKeyguardShowing -> - stateInternal(isKeyguardShowing) + override val state: Flow = conflatedCallbackFlow { + val callback = + object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback { + override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) { + trySendWithFailureLogging( + state( + isFeatureEnabled = walletController.isWalletEnabled, + hasCard = response?.walletCards?.isNotEmpty() == true, + tileIcon = walletController.walletClient.tileIcon, + ), + TAG, + ) + } + + override fun onWalletCardRetrievalError(error: GetWalletCardsError?) { + Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"") + trySendWithFailureLogging( + KeyguardQuickAffordanceConfig.State.Hidden, + TAG, + ) + } } + walletController.setupWalletChangeObservers( + callback, + QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, + QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE + ) + walletController.updateWalletPreference() + walletController.queryWalletCards(callback) + + awaitClose { + walletController.unregisterWalletChangeObservers( + QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, + QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE + ) + } + } + override fun onQuickAffordanceClicked( animationController: ActivityLaunchAnimator.Controller?, ): KeyguardQuickAffordanceConfig.OnClickedResult { @@ -66,53 +93,6 @@ constructor( return KeyguardQuickAffordanceConfig.OnClickedResult.Handled } - private fun stateInternal( - isKeyguardShowing: Boolean - ): Flow { - if (!isKeyguardShowing) { - return flowOf(KeyguardQuickAffordanceConfig.State.Hidden) - } - - return conflatedCallbackFlow { - val callback = - object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback { - override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) { - trySendWithFailureLogging( - state( - isFeatureEnabled = walletController.isWalletEnabled, - hasCard = response?.walletCards?.isNotEmpty() == true, - tileIcon = walletController.walletClient.tileIcon, - ), - TAG, - ) - } - - override fun onWalletCardRetrievalError(error: GetWalletCardsError?) { - Log.e(TAG, "Wallet card retrieval error, message: \"${error?.message}\"") - trySendWithFailureLogging( - KeyguardQuickAffordanceConfig.State.Hidden, - TAG, - ) - } - } - - walletController.setupWalletChangeObservers( - callback, - QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, - QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE - ) - walletController.updateWalletPreference() - walletController.queryWalletCards(callback) - - awaitClose { - walletController.unregisterWalletChangeObservers( - QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE, - QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE - ) - } - } - } - private fun state( isFeatureEnabled: Boolean, hasCard: Boolean, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index be91e51f85508..62cf1a624d9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -21,6 +21,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall import com.android.systemui.common.data.model.Position import com.android.systemui.dagger.SysUISingleton import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.policy.KeyguardStateController import javax.inject.Inject import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow @@ -49,6 +50,15 @@ interface KeyguardRepository { */ val clockPosition: StateFlow + /** + * Observable for whether the keyguard is showing. + * + * Note: this is also `true` when the lock-screen is occluded with an `Activity` "above" it in + * the z-order (which is not really above the system UI window, but rather - the lock-screen + * becomes invisible to reveal the "occluding activity"). + */ + val isKeyguardShowing: Flow + /** * Observable for whether we are in doze state. * @@ -91,6 +101,7 @@ class KeyguardRepositoryImpl @Inject constructor( statusBarStateController: StatusBarStateController, + keyguardStateController: KeyguardStateController, ) : KeyguardRepository { private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) override val animateBottomAreaDozingTransitions = @@ -102,6 +113,29 @@ constructor( private val _clockPosition = MutableStateFlow(Position(0, 0)) override val clockPosition = _clockPosition.asStateFlow() + override val isKeyguardShowing: Flow = conflatedCallbackFlow { + val callback = + object : KeyguardStateController.Callback { + override fun onKeyguardShowingChanged() { + trySendWithFailureLogging( + keyguardStateController.isShowing, + TAG, + "updated isKeyguardShowing" + ) + } + } + + keyguardStateController.addCallback(callback) + // Adding the callback does not send an initial update. + trySendWithFailureLogging( + keyguardStateController.isShowing, + TAG, + "initial isKeyguardShowing" + ) + + awaitClose { keyguardStateController.removeCallback(callback) } + } + override val isDozing: Flow = conflatedCallbackFlow { val callback = object : StatusBarStateController.StateListener { 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 d2ab3e9b0f879..1a5670c7e8077 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 @@ -30,7 +30,8 @@ interface KeyguardRepositoryModule { impl: KeyguardQuickAffordanceRepositoryImpl ): KeyguardQuickAffordanceRepository - @Binds fun keyguardQuickAffordanceConfigs( + @Binds + fun keyguardQuickAffordanceConfigs( impl: KeyguardQuickAffordanceConfigsImpl ): KeyguardQuickAffordanceConfigs } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsKeyguardShowingUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsKeyguardShowingUseCase.kt new file mode 100644 index 0000000000000..11af123c1650e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveIsKeyguardShowingUseCase.kt @@ -0,0 +1,39 @@ +/* + * 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.usecase + +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow + +/** + * Use-case for observing whether the keyguard is currently being shown. + * + * Note: this is also `true` when the lock-screen is occluded with an `Activity` "above" it in the + * z-order (which is not really above the system UI window, but rather - the lock-screen becomes + * invisible to reveal the "occluding activity"). + */ +class ObserveIsKeyguardShowingUseCase +@Inject +constructor( + private val repository: KeyguardRepository, +) { + operator fun invoke(): Flow { + return repository.isKeyguardShowing + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt index aac3d752ce706..eef8ec3e68f56 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCase.kt @@ -29,7 +29,7 @@ class ObserveKeyguardQuickAffordanceUseCase constructor( private val repository: KeyguardQuickAffordanceRepository, private val isDozingUseCase: ObserveIsDozingUseCase, - private val dozeAmountUseCase: ObserveDozeAmountUseCase, + private val isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase, ) { operator fun invoke( position: KeyguardQuickAffordancePosition @@ -37,9 +37,9 @@ constructor( return combine( repository.affordance(position), isDozingUseCase(), - dozeAmountUseCase(), - ) { affordance, isDozing, dozeAmount -> - if (!isDozing && dozeAmount == 0f) { + isKeyguardShowingUseCase(), + ) { affordance, isDozing, isKeyguardShowing -> + if (!isDozing && isKeyguardShowing) { affordance } else { KeyguardQuickAffordanceModel.Hidden diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerExt.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerExt.kt deleted file mode 100644 index b3f1eebb420b0..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerExt.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.statusbar.policy - -import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging -import com.android.systemui.common.coroutine.ConflatedCallbackFlow -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow - -object KeyguardStateControllerExt { - /** - * Returns an observable for whether the keyguard is currently shown or not. - */ - fun KeyguardStateController.isKeyguardShowing(loggingTag: String): Flow { - return ConflatedCallbackFlow.conflatedCallbackFlow { - val callback = - object : KeyguardStateController.Callback { - override fun onKeyguardShowingChanged() { - trySendWithFailureLogging( - isShowing, loggingTag, "updated isKeyguardShowing") - } - } - - addCallback(callback) - // Adding the callback does not send an initial update. - trySendWithFailureLogging(isShowing, loggingTag, "initial isKeyguardShowing") - - awaitClose { removeCallback(callback) } - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt index 7d1cccb8a0a80..10d2e4de631d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardQuickAffordanceRepository.kt @@ -27,7 +27,7 @@ class FakeKeyguardQuickAffordanceRepository : KeyguardQuickAffordanceRepository private val modelByPosition = mutableMapOf< - KeyguardQuickAffordancePosition, MutableStateFlow>() + KeyguardQuickAffordancePosition, MutableStateFlow>() init { KeyguardQuickAffordancePosition.values().forEach { value -> diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index c82803af4f377..d40b985f64dea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -17,9 +17,7 @@ package com.android.systemui.keyguard.data.repository import com.android.systemui.common.data.model.Position -import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -36,18 +34,13 @@ class FakeKeyguardRepository : KeyguardRepository { private val _clockPosition = MutableStateFlow(Position(0, 0)) override val clockPosition: StateFlow = _clockPosition - private val _isDozing = - MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) + private val _isKeyguardShowing = MutableStateFlow(false) + override val isKeyguardShowing: Flow = _isKeyguardShowing + + private val _isDozing = MutableStateFlow(false) override val isDozing: Flow = _isDozing - private val _dozeAmount = - MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) + private val _dozeAmount = MutableStateFlow(0f) override val dozeAmount: Flow = _dozeAmount init { @@ -67,11 +60,15 @@ class FakeKeyguardRepository : KeyguardRepository { _clockPosition.value = Position(x, y) } + fun setKeyguardShowing(isShowing: Boolean) { + _isKeyguardShowing.value = isShowing + } + fun setDozing(isDozing: Boolean) { - _isDozing.tryEmit(isDozing) + _isDozing.value = isDozing } fun setDozeAmount(dozeAmount: Float) { - _dozeAmount.tryEmit(dozeAmount) + _dozeAmount.value = dozeAmount } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt index 2ee80349ff4cb..3d2c51a449c7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.data.model.Position import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.argumentCaptor import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.launchIn @@ -31,6 +32,7 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import org.mockito.Mock import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @SmallTest @@ -38,6 +40,7 @@ import org.mockito.MockitoAnnotations class KeyguardRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var keyguardStateController: KeyguardStateController private lateinit var underTest: KeyguardRepositoryImpl @@ -45,7 +48,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - underTest = KeyguardRepositoryImpl(statusBarStateController) + underTest = KeyguardRepositoryImpl(statusBarStateController, keyguardStateController) } @Test @@ -99,6 +102,28 @@ class KeyguardRepositoryImplTest : SysuiTestCase() { assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1)) } + @Test + fun isKeyguardShowing() = runBlockingTest { + whenever(keyguardStateController.isShowing).thenReturn(false) + var latest: Boolean? = null + val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this) + + assertThat(latest).isFalse() + + val captor = argumentCaptor() + verify(keyguardStateController).addCallback(captor.capture()) + + whenever(keyguardStateController.isShowing).thenReturn(true) + captor.value.onKeyguardShowingChanged() + assertThat(latest).isTrue() + + whenever(keyguardStateController.isShowing).thenReturn(false) + captor.value.onKeyguardShowingChanged() + assertThat(latest).isFalse() + + job.cancel() + } + @Test fun isDozing() = runBlockingTest { var latest: Boolean? = null diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt index 4bef00a13a073..6e0da1b3fce15 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QrCodeScannerKeyguardQuickAffordanceConfigTest.kt @@ -51,7 +51,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) whenever(controller.intent).thenReturn(INTENT_1) - underTest = QrCodeScannerKeyguardQuickAffordanceConfig(context, controller) + underTest = QrCodeScannerKeyguardQuickAffordanceConfig(controller) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt index ee1d4d895e59d..79949721a8135 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/QuickAccessWalletKeyguardQuickAffordanceConfigTest.kt @@ -26,7 +26,6 @@ import com.android.systemui.containeddrawable.ContainedDrawable import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig import com.android.systemui.plugins.ActivityStarter -import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.wallet.controller.QuickAccessWalletController @@ -48,7 +47,6 @@ import org.mockito.MockitoAnnotations class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { @Mock private lateinit var walletController: QuickAccessWalletController - @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var activityStarter: ActivityStarter private lateinit var underTest: QuickAccessWalletKeyguardQuickAffordanceConfig @@ -59,7 +57,6 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { underTest = QuickAccessWalletKeyguardQuickAffordanceConfig( - keyguardStateController, walletController, activityStarter, ) @@ -67,7 +64,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { @Test fun `affordance - keyguard showing - has wallet card - visible model`() = runBlockingTest { - val callback = setUpState() + setUpState() var latest: KeyguardQuickAffordanceConfig.State? = null val job = underTest.state.onEach { latest = it }.launchIn(this) @@ -76,25 +73,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { assertThat(visibleModel.icon).isEqualTo(ContainedDrawable.WithDrawable(ICON)) assertThat(visibleModel.contentDescriptionResourceId).isNotNull() job.cancel() - callback?.let { verify(keyguardStateController).removeCallback(it) } - } - - @Test - fun `affordance - keyguard not showing - model is none`() = runBlockingTest { - val callback = setUpState(isKeyguardShowing = false) - var latest: KeyguardQuickAffordanceConfig.State? = null - - val job = underTest.state.onEach { latest = it }.launchIn(this) - - assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) - - job.cancel() - callback?.let { verify(keyguardStateController).removeCallback(it) } } @Test fun `affordance - wallet not enabled - model is none`() = runBlockingTest { - val callback = setUpState(isWalletEnabled = false) + setUpState(isWalletEnabled = false) var latest: KeyguardQuickAffordanceConfig.State? = null val job = underTest.state.onEach { latest = it }.launchIn(this) @@ -102,12 +85,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) job.cancel() - callback?.let { verify(keyguardStateController).removeCallback(it) } } @Test fun `affordance - query not successful - model is none`() = runBlockingTest { - val callback = setUpState(isWalletQuerySuccessful = false) + setUpState(isWalletQuerySuccessful = false) var latest: KeyguardQuickAffordanceConfig.State? = null val job = underTest.state.onEach { latest = it }.launchIn(this) @@ -115,12 +97,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) job.cancel() - callback?.let { verify(keyguardStateController).removeCallback(it) } } @Test fun `affordance - missing icon - model is none`() = runBlockingTest { - val callback = setUpState(hasWalletIcon = false) + setUpState(hasWalletIcon = false) var latest: KeyguardQuickAffordanceConfig.State? = null val job = underTest.state.onEach { latest = it }.launchIn(this) @@ -128,12 +109,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) job.cancel() - callback?.let { verify(keyguardStateController).removeCallback(it) } } @Test fun `affordance - no selected card - model is none`() = runBlockingTest { - val callback = setUpState(hasWalletIcon = false) + setUpState(hasWalletIcon = false) var latest: KeyguardQuickAffordanceConfig.State? = null val job = underTest.state.onEach { latest = it }.launchIn(this) @@ -141,7 +121,6 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) job.cancel() - callback?.let { verify(keyguardStateController).removeCallback(it) } } @Test @@ -159,21 +138,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } private fun setUpState( - isKeyguardShowing: Boolean = true, isWalletEnabled: Boolean = true, isWalletQuerySuccessful: Boolean = true, hasWalletIcon: Boolean = true, hasSelectedCard: Boolean = true, - ): KeyguardStateController.Callback? { - var returnedCallback: KeyguardStateController.Callback? = null - whenever(keyguardStateController.isShowing).thenReturn(isKeyguardShowing) - whenever(keyguardStateController.addCallback(any())).thenAnswer { invocation -> - with(invocation.arguments[0] as KeyguardStateController.Callback) { - returnedCallback = this - onKeyguardShowingChanged() - } - } - + ) { whenever(walletController.isWalletEnabled).thenReturn(isWalletEnabled) val walletClient: QuickAccessWalletClient = mock() @@ -203,8 +172,6 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { } } } - - return returnedCallback } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt index a60657c594c96..b90400be16d8f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/usecase/ObserveKeyguardQuickAffordanceUseCaseTest.kt @@ -43,20 +43,21 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() { private lateinit var repository: FakeKeyguardRepository private lateinit var quickAffordanceRepository: FakeKeyguardQuickAffordanceRepository private lateinit var isDozingUseCase: ObserveIsDozingUseCase - private lateinit var dozeAmountUseCase: ObserveDozeAmountUseCase + private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase @Before fun setUp() { repository = FakeKeyguardRepository() + repository.setKeyguardShowing(true) isDozingUseCase = ObserveIsDozingUseCase(repository) - dozeAmountUseCase = ObserveDozeAmountUseCase(repository) + isKeyguardShowingUseCase = ObserveIsKeyguardShowingUseCase(repository) quickAffordanceRepository = FakeKeyguardQuickAffordanceRepository() underTest = ObserveKeyguardQuickAffordanceUseCase( repository = quickAffordanceRepository, isDozingUseCase = isDozingUseCase, - dozeAmountUseCase = dozeAmountUseCase, + isKeyguardShowingUseCase = isKeyguardShowingUseCase, ) } @@ -75,9 +76,10 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() { ) var latest: KeyguardQuickAffordanceModel? = null - val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) - .onEach { latest = it } - .launchIn(this) + val job = + underTest(KeyguardQuickAffordancePosition.BOTTOM_END) + .onEach { latest = it } + .launchIn(this) assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = latest as KeyguardQuickAffordanceModel.Visible @@ -104,16 +106,17 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() { ) var latest: KeyguardQuickAffordanceModel? = null - val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) - .onEach { latest = it } - .launchIn(this) + val job = + underTest(KeyguardQuickAffordancePosition.BOTTOM_END) + .onEach { latest = it } + .launchIn(this) assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) job.cancel() } @Test - fun `invoke - affordance not visible doze amount is not 0`() = runBlockingTest { - repository.setDozeAmount(0.3f) + fun `invoke - affordance not visible when lockscreen is not showing`() = runBlockingTest { + repository.setKeyguardShowing(false) val configKey = HomeControlsKeyguardQuickAffordanceConfig::class val model = KeyguardQuickAffordanceModel.Visible( @@ -127,9 +130,10 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() { ) var latest: KeyguardQuickAffordanceModel? = null - val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) - .onEach { latest = it } - .launchIn(this) + val job = + underTest(KeyguardQuickAffordancePosition.BOTTOM_END) + .onEach { latest = it } + .launchIn(this) assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) job.cancel() } @@ -142,9 +146,10 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() { ) var latest: KeyguardQuickAffordanceModel? = null - val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_START) - .onEach { latest = it } - .launchIn(this) + val job = + underTest(KeyguardQuickAffordancePosition.BOTTOM_START) + .onEach { latest = it } + .launchIn(this) assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) job.cancel() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index cb9cbbaa169ff..00dd58e191421 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.domain.usecase.ObserveBottomAreaAlphaUseCas import com.android.systemui.keyguard.domain.usecase.ObserveClockPositionUseCase import com.android.systemui.keyguard.domain.usecase.ObserveDozeAmountUseCase import com.android.systemui.keyguard.domain.usecase.ObserveIsDozingUseCase +import com.android.systemui.keyguard.domain.usecase.ObserveIsKeyguardShowingUseCase import com.android.systemui.keyguard.domain.usecase.ObserveKeyguardQuickAffordanceUseCase import com.android.systemui.keyguard.domain.usecase.OnKeyguardQuickAffordanceClickedUseCase import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel @@ -65,7 +66,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { private lateinit var affordanceRepository: FakeKeyguardQuickAffordanceRepository private lateinit var repository: FakeKeyguardRepository private lateinit var isDozingUseCase: ObserveIsDozingUseCase - private lateinit var dozeAmountUseCase: ObserveDozeAmountUseCase + private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase private lateinit var launchQuickAffordanceUseCase: FakeLaunchKeyguardQuickAffordanceUseCase private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig @@ -83,8 +84,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { ObserveIsDozingUseCase( repository = repository, ) - dozeAmountUseCase = - ObserveDozeAmountUseCase( + isKeyguardShowingUseCase = + ObserveIsKeyguardShowingUseCase( repository = repository, ) launchQuickAffordanceUseCase = FakeLaunchKeyguardQuickAffordanceUseCase() @@ -98,7 +99,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { ObserveKeyguardQuickAffordanceUseCase( repository = affordanceRepository, isDozingUseCase = isDozingUseCase, - dozeAmountUseCase = dozeAmountUseCase, + isKeyguardShowingUseCase = isKeyguardShowingUseCase, ), onQuickAffordanceClickedUseCase = OnKeyguardQuickAffordanceClickedUseCase( @@ -140,12 +141,13 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun `startButton - present and not dozing - visible model - starts activity on click`() = + fun `startButton - present - not dozing - lockscreen showing - visible model - starts activity on click`() = // ktlint-disable max-line-length runBlockingTest { var latest: KeyguardQuickAffordanceViewModel? = null val job = underTest.startButton.onEach { latest = it }.launchIn(this) repository.setDozing(false) + repository.setKeyguardShowing(true) val testConfig = TestConfig( isVisible = true, @@ -168,12 +170,13 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun `endButton - present and not dozing - visible model - do nothing on click`() = + fun `endButton - present - not dozing - lockscreen showing - visible model - do nothing on click`() = // ktlint-disable max-line-length runBlockingTest { var latest: KeyguardQuickAffordanceViewModel? = null val job = underTest.endButton.onEach { latest = it }.launchIn(this) repository.setDozing(false) + repository.setKeyguardShowing(true) val config = TestConfig( isVisible = true, @@ -197,35 +200,38 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { } @Test - fun `startButton - not present and not dozing - model is none`() = runBlockingTest { - var latest: KeyguardQuickAffordanceViewModel? = null - val job = underTest.startButton.onEach { latest = it }.launchIn(this) + fun `startButton - not present - not dozing - lockscreen showing - model is none`() = + runBlockingTest { + var latest: KeyguardQuickAffordanceViewModel? = null + val job = underTest.startButton.onEach { latest = it }.launchIn(this) - repository.setDozing(false) - val config = - TestConfig( - isVisible = false, - ) - val configKey = - setUpQuickAffordanceModel( - position = KeyguardQuickAffordancePosition.BOTTOM_START, + repository.setDozing(false) + repository.setKeyguardShowing(true) + val config = + TestConfig( + isVisible = false, + ) + val configKey = + setUpQuickAffordanceModel( + position = KeyguardQuickAffordancePosition.BOTTOM_START, + testConfig = config, + ) + + assertQuickAffordanceViewModel( + viewModel = latest, testConfig = config, + configKey = configKey, ) - - assertQuickAffordanceViewModel( - viewModel = latest, - testConfig = config, - configKey = configKey, - ) - job.cancel() - } + job.cancel() + } @Test - fun `startButton - present but dozing - model is none`() = runBlockingTest { + fun `startButton - present - dozing - lockscreen showing - model is none`() = runBlockingTest { var latest: KeyguardQuickAffordanceViewModel? = null val job = underTest.startButton.onEach { latest = it }.launchIn(this) repository.setDozing(true) + repository.setKeyguardShowing(true) val config = TestConfig( isVisible = true, @@ -247,6 +253,35 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { job.cancel() } + @Test + fun `startButton - present - not dozing - lockscreen not showing - model is none`() = + runBlockingTest { + var latest: KeyguardQuickAffordanceViewModel? = null + val job = underTest.startButton.onEach { latest = it }.launchIn(this) + + repository.setDozing(false) + repository.setKeyguardShowing(false) + val config = + TestConfig( + isVisible = true, + icon = mock(), + canShowWhileLocked = false, + intent = Intent("action"), + ) + val configKey = + setUpQuickAffordanceModel( + position = KeyguardQuickAffordancePosition.BOTTOM_START, + testConfig = config, + ) + + assertQuickAffordanceViewModel( + viewModel = latest, + testConfig = TestConfig(isVisible = false), + configKey = configKey, + ) + job.cancel() + } + @Test fun animateButtonReveal() = runBlockingTest { val values = mutableListOf() @@ -287,6 +322,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { @Test fun isIndicationAreaPadded() = runBlockingTest { + repository.setKeyguardShowing(true) val values = mutableListOf() val job = underTest.isIndicationAreaPadded.onEach(values::add).launchIn(this)