Merge "Fixes quick affordance visibility bug." into tm-qpr-dev

This commit is contained in:
Alejandro Nijamkin
2022-08-02 21:20:38 +00:00
committed by Android (Google) Code Review
14 changed files with 245 additions and 212 deletions

View File

@@ -17,14 +17,12 @@
package com.android.systemui.keyguard.data.quickaffordance package com.android.systemui.keyguard.data.quickaffordance
import android.content.Context
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.containeddrawable.ContainedDrawable import com.android.systemui.containeddrawable.ContainedDrawable
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
@@ -35,12 +33,9 @@ import kotlinx.coroutines.flow.Flow
class QrCodeScannerKeyguardQuickAffordanceConfig class QrCodeScannerKeyguardQuickAffordanceConfig
@Inject @Inject
constructor( constructor(
@Application context: Context,
private val controller: QRCodeScannerController, private val controller: QRCodeScannerController,
) : KeyguardQuickAffordanceConfig { ) : KeyguardQuickAffordanceConfig {
private val appContext = context.applicationContext
override val state: Flow<KeyguardQuickAffordanceConfig.State> = conflatedCallbackFlow { override val state: Flow<KeyguardQuickAffordanceConfig.State> = conflatedCallbackFlow {
val callback = val callback =
object : QRCodeScannerController.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.containeddrawable.ContainedDrawable
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.ActivityStarter 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 com.android.systemui.wallet.controller.QuickAccessWalletController
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
/** Quick access wallet quick affordance data source. */ /** Quick access wallet quick affordance data source. */
@SysUISingleton @SysUISingleton
class QuickAccessWalletKeyguardQuickAffordanceConfig class QuickAccessWalletKeyguardQuickAffordanceConfig
@Inject @Inject
constructor( constructor(
private val keyguardStateController: KeyguardStateController,
private val walletController: QuickAccessWalletController, private val walletController: QuickAccessWalletController,
private val activityStarter: ActivityStarter, private val activityStarter: ActivityStarter,
) : KeyguardQuickAffordanceConfig { ) : KeyguardQuickAffordanceConfig {
override val state: Flow<KeyguardQuickAffordanceConfig.State> = override val state: Flow<KeyguardQuickAffordanceConfig.State> = conflatedCallbackFlow {
keyguardStateController val callback =
.isKeyguardShowing(TAG) object : QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
.flatMapLatest { isKeyguardShowing -> override fun onWalletCardsRetrieved(response: GetWalletCardsResponse?) {
stateInternal(isKeyguardShowing) 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( override fun onQuickAffordanceClicked(
animationController: ActivityLaunchAnimator.Controller?, animationController: ActivityLaunchAnimator.Controller?,
): KeyguardQuickAffordanceConfig.OnClickedResult { ): KeyguardQuickAffordanceConfig.OnClickedResult {
@@ -66,53 +93,6 @@ constructor(
return KeyguardQuickAffordanceConfig.OnClickedResult.Handled 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( private fun state(
isFeatureEnabled: Boolean, isFeatureEnabled: Boolean,
hasCard: 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.common.data.model.Position
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@@ -49,6 +50,15 @@ interface KeyguardRepository {
*/ */
val clockPosition: StateFlow<Position> 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. * Observable for whether we are in doze state.
* *
@@ -91,6 +101,7 @@ class KeyguardRepositoryImpl
@Inject @Inject
constructor( constructor(
statusBarStateController: StatusBarStateController, statusBarStateController: StatusBarStateController,
keyguardStateController: KeyguardStateController,
) : KeyguardRepository { ) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false) private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions = override val animateBottomAreaDozingTransitions =
@@ -102,6 +113,29 @@ constructor(
private val _clockPosition = MutableStateFlow(Position(0, 0)) private val _clockPosition = MutableStateFlow(Position(0, 0))
override val clockPosition = _clockPosition.asStateFlow() 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 { override val isDozing: Flow<Boolean> = conflatedCallbackFlow {
val callback = val callback =
object : StatusBarStateController.StateListener { object : StatusBarStateController.StateListener {

View File

@@ -30,7 +30,8 @@ interface KeyguardRepositoryModule {
impl: KeyguardQuickAffordanceRepositoryImpl impl: KeyguardQuickAffordanceRepositoryImpl
): KeyguardQuickAffordanceRepository ): KeyguardQuickAffordanceRepository
@Binds fun keyguardQuickAffordanceConfigs( @Binds
fun keyguardQuickAffordanceConfigs(
impl: KeyguardQuickAffordanceConfigsImpl impl: KeyguardQuickAffordanceConfigsImpl
): KeyguardQuickAffordanceConfigs ): 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( constructor(
private val repository: KeyguardQuickAffordanceRepository, private val repository: KeyguardQuickAffordanceRepository,
private val isDozingUseCase: ObserveIsDozingUseCase, private val isDozingUseCase: ObserveIsDozingUseCase,
private val dozeAmountUseCase: ObserveDozeAmountUseCase, private val isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase,
) { ) {
operator fun invoke( operator fun invoke(
position: KeyguardQuickAffordancePosition position: KeyguardQuickAffordancePosition
@@ -37,9 +37,9 @@ constructor(
return combine( return combine(
repository.affordance(position), repository.affordance(position),
isDozingUseCase(), isDozingUseCase(),
dozeAmountUseCase(), isKeyguardShowingUseCase(),
) { affordance, isDozing, dozeAmount -> ) { affordance, isDozing, isKeyguardShowing ->
if (!isDozing && dozeAmount == 0f) { if (!isDozing && isKeyguardShowing) {
affordance affordance
} else { } else {
KeyguardQuickAffordanceModel.Hidden 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 = private val modelByPosition =
mutableMapOf< mutableMapOf<
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>() KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
init { init {
KeyguardQuickAffordancePosition.values().forEach { value -> KeyguardQuickAffordancePosition.values().forEach { value ->

View File

@@ -17,9 +17,7 @@
package com.android.systemui.keyguard.data.repository package com.android.systemui.keyguard.data.repository
import com.android.systemui.common.data.model.Position import com.android.systemui.common.data.model.Position
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -36,18 +34,13 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _clockPosition = MutableStateFlow(Position(0, 0)) private val _clockPosition = MutableStateFlow(Position(0, 0))
override val clockPosition: StateFlow<Position> = _clockPosition override val clockPosition: StateFlow<Position> = _clockPosition
private val _isDozing = private val _isKeyguardShowing = MutableStateFlow(false)
MutableSharedFlow<Boolean>( override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST, private val _isDozing = MutableStateFlow(false)
)
override val isDozing: Flow<Boolean> = _isDozing override val isDozing: Flow<Boolean> = _isDozing
private val _dozeAmount = private val _dozeAmount = MutableStateFlow(0f)
MutableSharedFlow<Float>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
override val dozeAmount: Flow<Float> = _dozeAmount override val dozeAmount: Flow<Float> = _dozeAmount
init { init {
@@ -67,11 +60,15 @@ class FakeKeyguardRepository : KeyguardRepository {
_clockPosition.value = Position(x, y) _clockPosition.value = Position(x, y)
} }
fun setKeyguardShowing(isShowing: Boolean) {
_isKeyguardShowing.value = isShowing
}
fun setDozing(isDozing: Boolean) { fun setDozing(isDozing: Boolean) {
_isDozing.tryEmit(isDozing) _isDozing.value = isDozing
} }
fun setDozeAmount(dozeAmount: Float) { 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.SysuiTestCase
import com.android.systemui.common.data.model.Position import com.android.systemui.common.data.model.Position
import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.argumentCaptor
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@@ -31,6 +32,7 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4 import org.junit.runners.JUnit4
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@@ -38,6 +40,7 @@ import org.mockito.MockitoAnnotations
class KeyguardRepositoryImplTest : SysuiTestCase() { class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var keyguardStateController: KeyguardStateController
private lateinit var underTest: KeyguardRepositoryImpl private lateinit var underTest: KeyguardRepositoryImpl
@@ -45,7 +48,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
underTest = KeyguardRepositoryImpl(statusBarStateController) underTest = KeyguardRepositoryImpl(statusBarStateController, keyguardStateController)
} }
@Test @Test
@@ -99,6 +102,28 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1)) 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 @Test
fun isDozing() = runBlockingTest { fun isDozing() = runBlockingTest {
var latest: Boolean? = null var latest: Boolean? = null

View File

@@ -51,7 +51,7 @@ class QrCodeScannerKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
whenever(controller.intent).thenReturn(INTENT_1) whenever(controller.intent).thenReturn(INTENT_1)
underTest = QrCodeScannerKeyguardQuickAffordanceConfig(context, controller) underTest = QrCodeScannerKeyguardQuickAffordanceConfig(controller)
} }
@Test @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.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig
import com.android.systemui.plugins.ActivityStarter 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.any
import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.mock
import com.android.systemui.wallet.controller.QuickAccessWalletController import com.android.systemui.wallet.controller.QuickAccessWalletController
@@ -48,7 +47,6 @@ import org.mockito.MockitoAnnotations
class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() { class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Mock private lateinit var walletController: QuickAccessWalletController @Mock private lateinit var walletController: QuickAccessWalletController
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var activityStarter: ActivityStarter @Mock private lateinit var activityStarter: ActivityStarter
private lateinit var underTest: QuickAccessWalletKeyguardQuickAffordanceConfig private lateinit var underTest: QuickAccessWalletKeyguardQuickAffordanceConfig
@@ -59,7 +57,6 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
underTest = underTest =
QuickAccessWalletKeyguardQuickAffordanceConfig( QuickAccessWalletKeyguardQuickAffordanceConfig(
keyguardStateController,
walletController, walletController,
activityStarter, activityStarter,
) )
@@ -67,7 +64,7 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
@Test @Test
fun `affordance - keyguard showing - has wallet card - visible model`() = runBlockingTest { fun `affordance - keyguard showing - has wallet card - visible model`() = runBlockingTest {
val callback = setUpState() setUpState()
var latest: KeyguardQuickAffordanceConfig.State? = null var latest: KeyguardQuickAffordanceConfig.State? = null
val job = underTest.state.onEach { latest = it }.launchIn(this) 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.icon).isEqualTo(ContainedDrawable.WithDrawable(ICON))
assertThat(visibleModel.contentDescriptionResourceId).isNotNull() assertThat(visibleModel.contentDescriptionResourceId).isNotNull()
job.cancel() 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 @Test
fun `affordance - wallet not enabled - model is none`() = runBlockingTest { fun `affordance - wallet not enabled - model is none`() = runBlockingTest {
val callback = setUpState(isWalletEnabled = false) setUpState(isWalletEnabled = false)
var latest: KeyguardQuickAffordanceConfig.State? = null var latest: KeyguardQuickAffordanceConfig.State? = null
val job = underTest.state.onEach { latest = it }.launchIn(this) val job = underTest.state.onEach { latest = it }.launchIn(this)
@@ -102,12 +85,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
job.cancel() job.cancel()
callback?.let { verify(keyguardStateController).removeCallback(it) }
} }
@Test @Test
fun `affordance - query not successful - model is none`() = runBlockingTest { fun `affordance - query not successful - model is none`() = runBlockingTest {
val callback = setUpState(isWalletQuerySuccessful = false) setUpState(isWalletQuerySuccessful = false)
var latest: KeyguardQuickAffordanceConfig.State? = null var latest: KeyguardQuickAffordanceConfig.State? = null
val job = underTest.state.onEach { latest = it }.launchIn(this) val job = underTest.state.onEach { latest = it }.launchIn(this)
@@ -115,12 +97,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
job.cancel() job.cancel()
callback?.let { verify(keyguardStateController).removeCallback(it) }
} }
@Test @Test
fun `affordance - missing icon - model is none`() = runBlockingTest { fun `affordance - missing icon - model is none`() = runBlockingTest {
val callback = setUpState(hasWalletIcon = false) setUpState(hasWalletIcon = false)
var latest: KeyguardQuickAffordanceConfig.State? = null var latest: KeyguardQuickAffordanceConfig.State? = null
val job = underTest.state.onEach { latest = it }.launchIn(this) val job = underTest.state.onEach { latest = it }.launchIn(this)
@@ -128,12 +109,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
job.cancel() job.cancel()
callback?.let { verify(keyguardStateController).removeCallback(it) }
} }
@Test @Test
fun `affordance - no selected card - model is none`() = runBlockingTest { fun `affordance - no selected card - model is none`() = runBlockingTest {
val callback = setUpState(hasWalletIcon = false) setUpState(hasWalletIcon = false)
var latest: KeyguardQuickAffordanceConfig.State? = null var latest: KeyguardQuickAffordanceConfig.State? = null
val job = underTest.state.onEach { latest = it }.launchIn(this) val job = underTest.state.onEach { latest = it }.launchIn(this)
@@ -141,7 +121,6 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceConfig.State.Hidden)
job.cancel() job.cancel()
callback?.let { verify(keyguardStateController).removeCallback(it) }
} }
@Test @Test
@@ -159,21 +138,11 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
} }
private fun setUpState( private fun setUpState(
isKeyguardShowing: Boolean = true,
isWalletEnabled: Boolean = true, isWalletEnabled: Boolean = true,
isWalletQuerySuccessful: Boolean = true, isWalletQuerySuccessful: Boolean = true,
hasWalletIcon: Boolean = true, hasWalletIcon: Boolean = true,
hasSelectedCard: 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) whenever(walletController.isWalletEnabled).thenReturn(isWalletEnabled)
val walletClient: QuickAccessWalletClient = mock() val walletClient: QuickAccessWalletClient = mock()
@@ -203,8 +172,6 @@ class QuickAccessWalletKeyguardQuickAffordanceConfigTest : SysuiTestCase() {
} }
} }
} }
return returnedCallback
} }
companion object { companion object {

View File

@@ -43,20 +43,21 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
private lateinit var repository: FakeKeyguardRepository private lateinit var repository: FakeKeyguardRepository
private lateinit var quickAffordanceRepository: FakeKeyguardQuickAffordanceRepository private lateinit var quickAffordanceRepository: FakeKeyguardQuickAffordanceRepository
private lateinit var isDozingUseCase: ObserveIsDozingUseCase private lateinit var isDozingUseCase: ObserveIsDozingUseCase
private lateinit var dozeAmountUseCase: ObserveDozeAmountUseCase private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase
@Before @Before
fun setUp() { fun setUp() {
repository = FakeKeyguardRepository() repository = FakeKeyguardRepository()
repository.setKeyguardShowing(true)
isDozingUseCase = ObserveIsDozingUseCase(repository) isDozingUseCase = ObserveIsDozingUseCase(repository)
dozeAmountUseCase = ObserveDozeAmountUseCase(repository) isKeyguardShowingUseCase = ObserveIsKeyguardShowingUseCase(repository)
quickAffordanceRepository = FakeKeyguardQuickAffordanceRepository() quickAffordanceRepository = FakeKeyguardQuickAffordanceRepository()
underTest = underTest =
ObserveKeyguardQuickAffordanceUseCase( ObserveKeyguardQuickAffordanceUseCase(
repository = quickAffordanceRepository, repository = quickAffordanceRepository,
isDozingUseCase = isDozingUseCase, isDozingUseCase = isDozingUseCase,
dozeAmountUseCase = dozeAmountUseCase, isKeyguardShowingUseCase = isKeyguardShowingUseCase,
) )
} }
@@ -75,9 +76,10 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
) )
var latest: KeyguardQuickAffordanceModel? = null var latest: KeyguardQuickAffordanceModel? = null
val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) val job =
.onEach { latest = it } underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
.launchIn(this) .onEach { latest = it }
.launchIn(this)
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
@@ -104,16 +106,17 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
) )
var latest: KeyguardQuickAffordanceModel? = null var latest: KeyguardQuickAffordanceModel? = null
val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) val job =
.onEach { latest = it } underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
.launchIn(this) .onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel() job.cancel()
} }
@Test @Test
fun `invoke - affordance not visible doze amount is not 0`() = runBlockingTest { fun `invoke - affordance not visible when lockscreen is not showing`() = runBlockingTest {
repository.setDozeAmount(0.3f) repository.setKeyguardShowing(false)
val configKey = HomeControlsKeyguardQuickAffordanceConfig::class val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
val model = val model =
KeyguardQuickAffordanceModel.Visible( KeyguardQuickAffordanceModel.Visible(
@@ -127,9 +130,10 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
) )
var latest: KeyguardQuickAffordanceModel? = null var latest: KeyguardQuickAffordanceModel? = null
val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_END) val job =
.onEach { latest = it } underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
.launchIn(this) .onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel() job.cancel()
} }
@@ -142,9 +146,10 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
) )
var latest: KeyguardQuickAffordanceModel? = null var latest: KeyguardQuickAffordanceModel? = null
val job = underTest(KeyguardQuickAffordancePosition.BOTTOM_START) val job =
.onEach { latest = it } underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
.launchIn(this) .onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden) assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel() 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.ObserveClockPositionUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveDozeAmountUseCase import com.android.systemui.keyguard.domain.usecase.ObserveDozeAmountUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveIsDozingUseCase 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.ObserveKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.OnKeyguardQuickAffordanceClickedUseCase import com.android.systemui.keyguard.domain.usecase.OnKeyguardQuickAffordanceClickedUseCase
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
@@ -65,7 +66,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
private lateinit var affordanceRepository: FakeKeyguardQuickAffordanceRepository private lateinit var affordanceRepository: FakeKeyguardQuickAffordanceRepository
private lateinit var repository: FakeKeyguardRepository private lateinit var repository: FakeKeyguardRepository
private lateinit var isDozingUseCase: ObserveIsDozingUseCase private lateinit var isDozingUseCase: ObserveIsDozingUseCase
private lateinit var dozeAmountUseCase: ObserveDozeAmountUseCase private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase
private lateinit var launchQuickAffordanceUseCase: FakeLaunchKeyguardQuickAffordanceUseCase private lateinit var launchQuickAffordanceUseCase: FakeLaunchKeyguardQuickAffordanceUseCase
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
@@ -83,8 +84,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
ObserveIsDozingUseCase( ObserveIsDozingUseCase(
repository = repository, repository = repository,
) )
dozeAmountUseCase = isKeyguardShowingUseCase =
ObserveDozeAmountUseCase( ObserveIsKeyguardShowingUseCase(
repository = repository, repository = repository,
) )
launchQuickAffordanceUseCase = FakeLaunchKeyguardQuickAffordanceUseCase() launchQuickAffordanceUseCase = FakeLaunchKeyguardQuickAffordanceUseCase()
@@ -98,7 +99,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
ObserveKeyguardQuickAffordanceUseCase( ObserveKeyguardQuickAffordanceUseCase(
repository = affordanceRepository, repository = affordanceRepository,
isDozingUseCase = isDozingUseCase, isDozingUseCase = isDozingUseCase,
dozeAmountUseCase = dozeAmountUseCase, isKeyguardShowingUseCase = isKeyguardShowingUseCase,
), ),
onQuickAffordanceClickedUseCase = onQuickAffordanceClickedUseCase =
OnKeyguardQuickAffordanceClickedUseCase( OnKeyguardQuickAffordanceClickedUseCase(
@@ -140,12 +141,13 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
} }
@Test @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 { runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this) val job = underTest.startButton.onEach { latest = it }.launchIn(this)
repository.setDozing(false) repository.setDozing(false)
repository.setKeyguardShowing(true)
val testConfig = val testConfig =
TestConfig( TestConfig(
isVisible = true, isVisible = true,
@@ -168,12 +170,13 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
} }
@Test @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 { runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.endButton.onEach { latest = it }.launchIn(this) val job = underTest.endButton.onEach { latest = it }.launchIn(this)
repository.setDozing(false) repository.setDozing(false)
repository.setKeyguardShowing(true)
val config = val config =
TestConfig( TestConfig(
isVisible = true, isVisible = true,
@@ -197,35 +200,38 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
} }
@Test @Test
fun `startButton - not present and not dozing - model is none`() = runBlockingTest { fun `startButton - not present - not dozing - lockscreen showing - model is none`() =
var latest: KeyguardQuickAffordanceViewModel? = null runBlockingTest {
val job = underTest.startButton.onEach { latest = it }.launchIn(this) var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this)
repository.setDozing(false) repository.setDozing(false)
val config = repository.setKeyguardShowing(true)
TestConfig( val config =
isVisible = false, TestConfig(
) isVisible = false,
val configKey = )
setUpQuickAffordanceModel( val configKey =
position = KeyguardQuickAffordancePosition.BOTTOM_START, setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
testConfig = config,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = config, testConfig = config,
configKey = configKey,
) )
job.cancel()
assertQuickAffordanceViewModel( }
viewModel = latest,
testConfig = config,
configKey = configKey,
)
job.cancel()
}
@Test @Test
fun `startButton - present but dozing - model is none`() = runBlockingTest { fun `startButton - present - dozing - lockscreen showing - model is none`() = runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this) val job = underTest.startButton.onEach { latest = it }.launchIn(this)
repository.setDozing(true) repository.setDozing(true)
repository.setKeyguardShowing(true)
val config = val config =
TestConfig( TestConfig(
isVisible = true, isVisible = true,
@@ -247,6 +253,35 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
job.cancel() 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 @Test
fun animateButtonReveal() = runBlockingTest { fun animateButtonReveal() = runBlockingTest {
val values = mutableListOf<Boolean>() val values = mutableListOf<Boolean>()
@@ -287,6 +322,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
@Test @Test
fun isIndicationAreaPadded() = runBlockingTest { fun isIndicationAreaPadded() = runBlockingTest {
repository.setKeyguardShowing(true)
val values = mutableListOf<Boolean>() val values = mutableListOf<Boolean>()
val job = underTest.isIndicationAreaPadded.onEach(values::add).launchIn(this) val job = underTest.isIndicationAreaPadded.onEach(values::add).launchIn(this)