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:
Alejandro Nijamkin
2022-08-01 12:50:45 -07:00
parent a9f3033fec
commit 99404d72b1
14 changed files with 245 additions and 212 deletions

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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 {

View File

@@ -30,7 +30,8 @@ interface KeyguardRepositoryModule {
impl: KeyguardQuickAffordanceRepositoryImpl
): KeyguardQuickAffordanceRepository
@Binds fun keyguardQuickAffordanceConfigs(
@Binds
fun keyguardQuickAffordanceConfigs(
impl: KeyguardQuickAffordanceConfigsImpl
): KeyguardQuickAffordanceConfigs
}

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -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) }
}
}
}

View File

@@ -27,7 +27,7 @@ class FakeKeyguardQuickAffordanceRepository : KeyguardQuickAffordanceRepository
private val modelByPosition =
mutableMapOf<
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
init {
KeyguardQuickAffordancePosition.values().forEach { value ->

View File

@@ -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
}
}

View File

@@ -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

View File

@@ -51,7 +51,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this)
whenever(controller.intent).thenReturn(INTENT_1)
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(context, controller)
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(controller)
}
@Test

View File

@@ -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 {

View File

@@ -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()
}

View File

@@ -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)