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
This commit is contained in:
@@ -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<KeyguardQuickAffordanceConfig.State> = conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : QRCodeScannerController.Callback {
|
||||
|
||||
@@ -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<KeyguardQuickAffordanceConfig.State> =
|
||||
keyguardStateController
|
||||
.isKeyguardShowing(TAG)
|
||||
.flatMapLatest { isKeyguardShowing ->
|
||||
stateInternal(isKeyguardShowing)
|
||||
override val state: Flow<KeyguardQuickAffordanceConfig.State> = 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<KeyguardQuickAffordanceConfig.State> {
|
||||
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,
|
||||
|
||||
@@ -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<Position>
|
||||
|
||||
/**
|
||||
* 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<Boolean>
|
||||
|
||||
/**
|
||||
* 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<Boolean> = 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<Boolean> = conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : StatusBarStateController.StateListener {
|
||||
|
||||
@@ -30,7 +30,8 @@ interface KeyguardRepositoryModule {
|
||||
impl: KeyguardQuickAffordanceRepositoryImpl
|
||||
): KeyguardQuickAffordanceRepository
|
||||
|
||||
@Binds fun keyguardQuickAffordanceConfigs(
|
||||
@Binds
|
||||
fun keyguardQuickAffordanceConfigs(
|
||||
impl: KeyguardQuickAffordanceConfigsImpl
|
||||
): KeyguardQuickAffordanceConfigs
|
||||
}
|
||||
|
||||
@@ -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<Boolean> {
|
||||
return repository.isKeyguardShowing
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Boolean> {
|
||||
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) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ class FakeKeyguardQuickAffordanceRepository : KeyguardQuickAffordanceRepository
|
||||
|
||||
private val modelByPosition =
|
||||
mutableMapOf<
|
||||
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
|
||||
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
|
||||
|
||||
init {
|
||||
KeyguardQuickAffordancePosition.values().forEach { value ->
|
||||
|
||||
@@ -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<Position> = _clockPosition
|
||||
|
||||
private val _isDozing =
|
||||
MutableSharedFlow<Boolean>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
private val _isKeyguardShowing = MutableStateFlow(false)
|
||||
override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
|
||||
|
||||
private val _isDozing = MutableStateFlow(false)
|
||||
override val isDozing: Flow<Boolean> = _isDozing
|
||||
|
||||
private val _dozeAmount =
|
||||
MutableSharedFlow<Float>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST,
|
||||
)
|
||||
private val _dozeAmount = MutableStateFlow(0f)
|
||||
override val dozeAmount: Flow<Float> = _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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<KeyguardStateController.Callback>()
|
||||
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
|
||||
|
||||
@@ -51,7 +51,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(controller.intent).thenReturn(INTENT_1)
|
||||
|
||||
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(context, controller)
|
||||
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(controller)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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<Boolean>()
|
||||
@@ -287,6 +322,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun isIndicationAreaPadded() = runBlockingTest {
|
||||
repository.setKeyguardShowing(true)
|
||||
val values = mutableListOf<Boolean>()
|
||||
val job = underTest.isIndicationAreaPadded.onEach(values::add).launchIn(this)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user