Merge "Moves quick affordances into domain layer." into tm-qpr-dev

This commit is contained in:
Alejandro Nijamkin
2022-08-12 00:38:43 +00:00
committed by Android (Google) Code Review
29 changed files with 433 additions and 676 deletions

View File

@@ -6,16 +6,16 @@ credit card, etc.
## Adding a new Quick Affordance
### Step 1: create a new quick affordance config
* Create a new class under the [systemui/keyguard/data/quickaffordance](../../src/com/android/systemui/keyguard/data/quickaffordance) directory
* Create a new class under the [systemui/keyguard/domain/quickaffordance](../../src/com/android/systemui/keyguard/domain/quickaffordance) directory
* Please make sure that the class is injected through the Dagger dependency injection system by using the `@Inject` annotation on its main constructor and the `@SysUISingleton` annotation at class level, to make sure only one instance of the class is ever instantiated
* Have the class implement the [KeyguardQuickAffordanceConfig](../../src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfig.kt) interface, notes:
* Have the class implement the [KeyguardQuickAffordanceConfig](../../src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceConfig.kt) interface, notes:
* The `state` Flow property must emit `State.Hidden` when the feature is not enabled!
* It is safe to assume that `onQuickAffordanceClicked` will not be invoked if-and-only-if the previous rule is followed
* When implementing `onQuickAffordanceClicked`, the implementation can do something or it can ask the framework to start an activity using an `Intent` provided by the implementation
* Please include a unit test for your new implementation under [the correct directory](../../tests/src/com/android/systemui/keyguard/data/quickaffordance)
* Please include a unit test for your new implementation under [the correct directory](../../tests/src/com/android/systemui/keyguard/domain/quickaffordance)
### Step 2: choose a position and priority
* Add the new class as a dependency in the constructor of [KeyguardQuickAffordanceConfigs](../../src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceConfigs.kt)
* Add the new class as a dependency in the constructor of [KeyguardQuickAffordanceRegistry](../../src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt)
* Place the new class in one of the available positions in the `configsByPosition` property, note:
* In each position, there is a list. The order matters. The order of that list is the priority order in which the framework considers each config. The first config whose state property returns `State.Visible` determines the button that is shown for that position
* Please only add to one position. The framework treats each position individually and there is no good way to prevent the same config from making its button appear in more than one position at the same time

View File

@@ -43,6 +43,7 @@ import com.android.systemui.keyguard.DismissCallbackRegistry;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
import com.android.systemui.keyguard.domain.usecase.KeyguardUseCaseModule;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -70,6 +71,7 @@ import dagger.Provides;
KeyguardUserSwitcherComponent.class},
includes = {
FalsingModule.class,
KeyguardQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
KeyguardUseCaseModule.class,
})

View File

@@ -1,65 +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.keyguard.data.repository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.State
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
/** Defines interface for classes that encapsulate quick affordance state for the keyguard. */
interface KeyguardQuickAffordanceRepository {
fun affordance(position: KeyguardQuickAffordancePosition): Flow<KeyguardQuickAffordanceModel>
}
/** Real implementation of [KeyguardQuickAffordanceRepository] */
@SysUISingleton
class KeyguardQuickAffordanceRepositoryImpl
@Inject
constructor(
private val configs: KeyguardQuickAffordanceConfigs,
) : KeyguardQuickAffordanceRepository {
/** Returns an observable for the quick affordance model in the given position. */
override fun affordance(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
val configs = configs.getAll(position)
return combine(configs.map { config -> config.state }) { states ->
val index = states.indexOfFirst { state -> state is State.Visible }
val visibleState =
if (index != -1) {
states[index] as State.Visible
} else {
null
}
if (visibleState != null) {
KeyguardQuickAffordanceModel.Visible(
configKey = configs[index]::class,
icon = visibleState.icon,
contentDescriptionResourceId = visibleState.contentDescriptionResourceId,
)
} else {
KeyguardQuickAffordanceModel.Hidden
}
}
}
}

View File

@@ -16,22 +16,10 @@
package com.android.systemui.keyguard.data.repository
import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigsImpl
import dagger.Binds
import dagger.Module
@Module
interface KeyguardRepositoryModule {
@Binds fun keyguardRepository(impl: KeyguardRepositoryImpl): KeyguardRepository
@Binds
fun keyguardQuickAffordanceRepository(
impl: KeyguardQuickAffordanceRepositoryImpl
): KeyguardQuickAffordanceRepository
@Binds
fun keyguardQuickAffordanceConfigs(
impl: KeyguardQuickAffordanceConfigsImpl
): KeyguardQuickAffordanceConfigs
}

View File

@@ -15,11 +15,11 @@
*
*/
package com.android.systemui.keyguard.shared.model
package com.android.systemui.keyguard.domain.model
import androidx.annotation.StringRes
import com.android.systemui.containeddrawable.ContainedDrawable
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import kotlin.reflect.KClass
/**
@@ -27,7 +27,6 @@ import kotlin.reflect.KClass
* lock-screen).
*/
sealed class KeyguardQuickAffordanceModel {
/** No affordance should show up. */
object Hidden : KeyguardQuickAffordanceModel()
@@ -43,4 +42,21 @@ sealed class KeyguardQuickAffordanceModel {
*/
@StringRes val contentDescriptionResourceId: Int,
) : KeyguardQuickAffordanceModel()
companion object {
fun from(
state: KeyguardQuickAffordanceConfig.State?,
configKey: KClass<out KeyguardQuickAffordanceConfig>,
): KeyguardQuickAffordanceModel {
return when (state) {
is KeyguardQuickAffordanceConfig.State.Visible ->
Visible(
configKey = configKey,
icon = state.icon,
contentDescriptionResourceId = state.contentDescriptionResourceId,
)
else -> Hidden
}
}
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.systemui.keyguard.shared.model
package com.android.systemui.keyguard.domain.model
/** Enumerates all possible positions for quick affordances that can appear on the lock-screen. */
enum class KeyguardQuickAffordancePosition {

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Context
import android.content.Intent

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Intent
import androidx.annotation.StringRes

View File

@@ -0,0 +1,29 @@
/*
* 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.quickaffordance
import dagger.Binds
import dagger.Module
@Module
interface KeyguardQuickAffordanceModule {
@Binds
fun keyguardQuickAffordanceRegistry(
impl: KeyguardQuickAffordanceRegistryImpl
): KeyguardQuickAffordanceRegistry
}

View File

@@ -15,29 +15,25 @@
*
*/
package com.android.systemui.keyguard.data.config
package com.android.systemui.keyguard.domain.quickaffordance
import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.QrCodeScannerKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import javax.inject.Inject
import kotlin.reflect.KClass
/** Injectable provider of the positioning of the known quick affordance configs. */
interface KeyguardQuickAffordanceConfigs {
/** Central registry of all known quick affordance configs. */
interface KeyguardQuickAffordanceRegistry {
fun getAll(position: KeyguardQuickAffordancePosition): List<KeyguardQuickAffordanceConfig>
fun get(configClass: KClass<out KeyguardQuickAffordanceConfig>): KeyguardQuickAffordanceConfig
}
class KeyguardQuickAffordanceConfigsImpl
class KeyguardQuickAffordanceRegistryImpl
@Inject
constructor(
homeControls: HomeControlsKeyguardQuickAffordanceConfig,
quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig,
) : KeyguardQuickAffordanceConfigs {
) : KeyguardQuickAffordanceRegistry {
private val configsByPosition =
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsError

View File

@@ -26,4 +26,9 @@ interface KeyguardUseCaseModule {
fun launchQuickAffordance(
impl: LaunchKeyguardQuickAffordanceUseCaseImpl
): LaunchKeyguardQuickAffordanceUseCase
@Binds
fun observeKeyguardQuickAffordance(
impl: ObserveKeyguardQuickAffordanceUseCaseImpl
): ObserveKeyguardQuickAffordanceUseCase
}

View File

@@ -16,26 +16,33 @@
package com.android.systemui.keyguard.domain.usecase
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
/** Use-case for observing the model of a quick affordance in the keyguard. */
class ObserveKeyguardQuickAffordanceUseCase
/** Defines interface for use-case for observing the model of a quick affordance in the keyguard. */
interface ObserveKeyguardQuickAffordanceUseCase {
operator fun invoke(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel>
}
class ObserveKeyguardQuickAffordanceUseCaseImpl
@Inject
constructor(
private val repository: KeyguardQuickAffordanceRepository,
private val registry: KeyguardQuickAffordanceRegistry,
private val isDozingUseCase: ObserveIsDozingUseCase,
private val isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase,
) {
operator fun invoke(
) : ObserveKeyguardQuickAffordanceUseCase {
override fun invoke(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
return combine(
repository.affordance(position),
affordance(position),
isDozingUseCase(),
isKeyguardShowingUseCase(),
) { affordance, isDozing, isKeyguardShowing ->
@@ -46,4 +53,23 @@ constructor(
}
}
}
private fun affordance(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
val configs = registry.getAll(position)
return combine(configs.map { config -> config.state }) { states ->
val index =
states.indexOfFirst { state ->
state is KeyguardQuickAffordanceConfig.State.Visible
}
val visibleState =
if (index != -1) {
states[index] as KeyguardQuickAffordanceConfig.State.Visible
} else {
null
}
KeyguardQuickAffordanceModel.from(visibleState, configs[index]::class)
}
}
}

View File

@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.domain.usecase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry
import javax.inject.Inject
import kotlin.reflect.KClass
@@ -27,7 +27,7 @@ import kotlin.reflect.KClass
class OnKeyguardQuickAffordanceClickedUseCase
@Inject
constructor(
private val configs: KeyguardQuickAffordanceConfigs,
private val registry: KeyguardQuickAffordanceRegistry,
private val launchAffordanceUseCase: LaunchKeyguardQuickAffordanceUseCase,
) {
operator fun invoke(
@@ -35,7 +35,7 @@ constructor(
animationController: ActivityLaunchAnimator.Controller?,
) {
@Suppress("UNCHECKED_CAST")
val config = configs.get(configKey as KClass<out KeyguardQuickAffordanceConfig>)
val config = registry.get(configKey as KClass<out KeyguardQuickAffordanceConfig>)
when (val result = config.onQuickAffordanceClicked(animationController)) {
is OnClickedResult.StartActivity ->
launchAffordanceUseCase(

View File

@@ -17,6 +17,8 @@
package com.android.systemui.keyguard.ui.viewmodel
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.usecase.ObserveAnimateBottomAreaTransitionsUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveBottomAreaAlphaUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveClockPositionUseCase
@@ -24,8 +26,6 @@ import com.android.systemui.keyguard.domain.usecase.ObserveDozeAmountUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveIsDozingUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.OnKeyguardQuickAffordanceClickedUseCase
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine

View File

@@ -1,44 +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.keyguard.data.repository
import com.android.systemui.keyguard.data.config.KeyguardQuickAffordanceConfigs
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import kotlin.reflect.KClass
/** Fake implementation of [KeyguardQuickAffordanceConfigs], for tests. */
class FakeKeyguardQuickAffordanceConfigs(
private val configsByPosition:
Map<KeyguardQuickAffordancePosition, List<KeyguardQuickAffordanceConfig>>,
) : KeyguardQuickAffordanceConfigs {
override fun getAll(
position: KeyguardQuickAffordancePosition
): List<KeyguardQuickAffordanceConfig> {
return configsByPosition.getValue(position)
}
override fun get(
configClass: KClass<out KeyguardQuickAffordanceConfig>
): KeyguardQuickAffordanceConfig {
return configsByPosition.values
.flatten()
.associateBy { config -> config::class }
.getValue(configClass)
}
}

View File

@@ -1,55 +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.keyguard.data.repository
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.yield
/** Fake implementation of [KeyguardQuickAffordanceRepository], for tests. */
class FakeKeyguardQuickAffordanceRepository : KeyguardQuickAffordanceRepository {
private val modelByPosition =
mutableMapOf<
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
init {
KeyguardQuickAffordancePosition.values().forEach { value ->
modelByPosition[value] = MutableStateFlow(KeyguardQuickAffordanceModel.Hidden)
}
}
override fun affordance(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
return modelByPosition.getValue(position)
}
suspend fun setModel(
position: KeyguardQuickAffordancePosition,
model: KeyguardQuickAffordanceModel
) {
modelByPosition.getValue(position).value = model
// Yield to allow the test's collection coroutine to "catch up" and collect this value
// before the test continues to the next line.
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
yield()
}
}

View File

@@ -20,6 +20,7 @@ import com.android.systemui.common.data.model.Position
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.yield
/** Fake implementation of [KeyguardRepository] */
class FakeKeyguardRepository : KeyguardRepository {
@@ -43,11 +44,6 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _dozeAmount = MutableStateFlow(0f)
override val dozeAmount: Flow<Float> = _dozeAmount
init {
setDozeAmount(0f)
setDozing(false)
}
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.tryEmit(animate)
}
@@ -60,15 +56,30 @@ class FakeKeyguardRepository : KeyguardRepository {
_clockPosition.value = Position(x, y)
}
fun setKeyguardShowing(isShowing: Boolean) {
suspend fun setKeyguardShowing(isShowing: Boolean) {
_isKeyguardShowing.value = isShowing
// Yield to allow the test's collection coroutine to "catch up" and collect this value
// before the test continues to the next line.
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
yield()
}
fun setDozing(isDozing: Boolean) {
suspend fun setDozing(isDozing: Boolean) {
_isDozing.value = isDozing
// Yield to allow the test's collection coroutine to "catch up" and collect this value
// before the test continues to the next line.
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
yield()
}
fun setDozeAmount(dozeAmount: Float) {
suspend fun setDozeAmount(dozeAmount: Float) {
_dozeAmount.value = dozeAmount
// Yield to allow the test's collection coroutine to "catch up" and collect this value
// before the test continues to the next line.
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection and remove this.
yield()
}
}

View File

@@ -1,193 +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.keyguard.data.repository
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlin.reflect.KClass
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(JUnit4::class)
class KeyguardQuickAffordanceRepositoryImplTest : SysuiTestCase() {
private lateinit var underTest: KeyguardQuickAffordanceRepository
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
quickAccessWallet = object : FakeKeyguardQuickAffordanceConfig() {}
qrCodeScanner = object : FakeKeyguardQuickAffordanceConfig() {}
underTest =
KeyguardQuickAffordanceRepositoryImpl(
configs =
FakeKeyguardQuickAffordanceConfigs(
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to
listOf(
homeControls,
),
KeyguardQuickAffordancePosition.BOTTOM_END to
listOf(
quickAccessWallet,
qrCodeScanner,
),
),
),
)
}
@Test
fun `bottom start affordance - none`() = runBlockingTest {
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest
.affordance(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel()
}
@Test
fun `bottom start affordance - home controls`() = runBlockingTest {
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest
.affordance(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
val state =
KeyguardQuickAffordanceConfig.State.Visible(
icon = mock(),
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
homeControls.setState(state)
assertThat(latest).isEqualTo(state.toModel(homeControls::class))
job.cancel()
}
@Test
fun `bottom end affordance - none`() = runBlockingTest {
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest
.affordance(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel()
}
@Test
fun `bottom end affordance - quick access wallet`() = runBlockingTest {
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest
.affordance(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
val quickAccessWalletState =
KeyguardQuickAffordanceConfig.State.Visible(
icon = mock(),
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
quickAccessWallet.setState(quickAccessWalletState)
val qrCodeScannerState =
KeyguardQuickAffordanceConfig.State.Visible(
icon = mock(),
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
qrCodeScanner.setState(qrCodeScannerState)
assertThat(latest).isEqualTo(quickAccessWalletState.toModel(quickAccessWallet::class))
job.cancel()
}
@Test
fun `bottom end affordance - qr code scanner`() = runBlockingTest {
// TODO(b/239834928): once coroutines.test is updated, switch to the approach described in
// https://developer.android.com/kotlin/flow/test#continuous-collection
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest
.affordance(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
val state =
KeyguardQuickAffordanceConfig.State.Visible(
icon = mock(),
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
qrCodeScanner.setState(state)
assertThat(latest).isEqualTo(state.toModel(qrCodeScanner::class))
job.cancel()
}
private fun KeyguardQuickAffordanceConfig.State?.toModel(
configKey: KClass<out KeyguardQuickAffordanceConfig>,
): KeyguardQuickAffordanceModel? {
return when (this) {
is KeyguardQuickAffordanceConfig.State.Visible ->
KeyguardQuickAffordanceModel.Visible(
configKey = configKey,
icon = icon,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
is KeyguardQuickAffordanceConfig.State.Hidden -> KeyguardQuickAffordanceModel.Hidden
null -> null
}
}
companion object {
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337
}
}

View File

@@ -1,24 +1,24 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* 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
* 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
* 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.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.keyguard.data.repository
package com.android.systemui.keyguard.domain.quickaffordance
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.yield
@@ -31,9 +31,6 @@ import kotlinx.coroutines.yield
*/
abstract class FakeKeyguardQuickAffordanceConfig : KeyguardQuickAffordanceConfig {
private val _onClickedInvocations = mutableListOf<ActivityLaunchAnimator.Controller?>()
val onClickedInvocations: List<ActivityLaunchAnimator.Controller?> = _onClickedInvocations
var onClickedResult: OnClickedResult = OnClickedResult.Handled
private val _state =
@@ -45,7 +42,6 @@ abstract class FakeKeyguardQuickAffordanceConfig : KeyguardQuickAffordanceConfig
override fun onQuickAffordanceClicked(
animationController: ActivityLaunchAnimator.Controller?,
): OnClickedResult {
_onClickedInvocations.add(animationController)
return onClickedResult
}

View File

@@ -0,0 +1,43 @@
/*
* 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.quickaffordance
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import kotlin.reflect.KClass
/** Fake implementation of [FakeKeyguardQuickAffordanceRegistry], for tests. */
class FakeKeyguardQuickAffordanceRegistry(
private val configsByPosition:
Map<KeyguardQuickAffordancePosition, List<KeyguardQuickAffordanceConfig>>,
) : KeyguardQuickAffordanceRegistry {
override fun getAll(
position: KeyguardQuickAffordancePosition
): List<KeyguardQuickAffordanceConfig> {
return configsByPosition.getValue(position)
}
override fun get(
configClass: KClass<out KeyguardQuickAffordanceConfig>
): KeyguardQuickAffordanceConfig {
return configsByPosition.values
.flatten()
.associateBy { config -> config::class }
.getValue(configClass)
}
}

View File

@@ -1,20 +1,21 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* 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
* 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
* 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.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import androidx.test.filters.SmallTest
import com.android.systemui.R

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -23,7 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import java.util.Optional

View File

@@ -15,12 +15,12 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import android.content.Intent
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig.OnClickedResult
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.keyguard.data.quickaffordance
package com.android.systemui.keyguard.domain.quickaffordance
import android.graphics.drawable.Drawable
import android.service.quickaccesswallet.GetWalletCardsResponse

View File

@@ -0,0 +1,46 @@
/*
* 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.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
class FakeObserveKeyguardQuickAffordanceUseCase : ObserveKeyguardQuickAffordanceUseCase {
private val affordanceByPosition =
mutableMapOf<
KeyguardQuickAffordancePosition, MutableStateFlow<KeyguardQuickAffordanceModel>>()
init {
KeyguardQuickAffordancePosition.values().forEach { position ->
affordanceByPosition[position] = MutableStateFlow(KeyguardQuickAffordanceModel.Hidden)
}
}
override fun invoke(
position: KeyguardQuickAffordancePosition
): Flow<KeyguardQuickAffordanceModel> {
return affordanceByPosition[position] ?: error("Flow unexpectedly missing!")
}
fun setModel(position: KeyguardQuickAffordancePosition, model: KeyguardQuickAffordanceModel) {
affordanceByPosition[position]?.value = model
}
}

View File

@@ -19,11 +19,12 @@ package com.android.systemui.keyguard.domain.usecase
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.containeddrawable.ContainedDrawable
import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
@@ -36,43 +37,82 @@ import org.junit.runners.JUnit4
@SmallTest
@RunWith(JUnit4::class)
class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
class ObserveKeyguardQuickAffordanceUseCaseImplTest : SysuiTestCase() {
private lateinit var underTest: ObserveKeyguardQuickAffordanceUseCase
private lateinit var repository: FakeKeyguardRepository
private lateinit var quickAffordanceRepository: FakeKeyguardQuickAffordanceRepository
private lateinit var isDozingUseCase: ObserveIsDozingUseCase
private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase
private lateinit var homeControls: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWallet: FakeKeyguardQuickAffordanceConfig
private lateinit var qrCodeScanner: FakeKeyguardQuickAffordanceConfig
@Before
fun setUp() {
fun setUp() = runBlockingTest {
repository = FakeKeyguardRepository()
repository.setKeyguardShowing(true)
isDozingUseCase = ObserveIsDozingUseCase(repository)
isKeyguardShowingUseCase = ObserveIsKeyguardShowingUseCase(repository)
quickAffordanceRepository = FakeKeyguardQuickAffordanceRepository()
homeControls = object : FakeKeyguardQuickAffordanceConfig() {}
quickAccessWallet = object : FakeKeyguardQuickAffordanceConfig() {}
qrCodeScanner = object : FakeKeyguardQuickAffordanceConfig() {}
underTest =
ObserveKeyguardQuickAffordanceUseCase(
repository = quickAffordanceRepository,
ObserveKeyguardQuickAffordanceUseCaseImpl(
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to
listOf(
homeControls,
),
KeyguardQuickAffordancePosition.BOTTOM_END to
listOf(
quickAccessWallet,
qrCodeScanner,
),
),
),
isDozingUseCase = isDozingUseCase,
isKeyguardShowingUseCase = isKeyguardShowingUseCase,
)
}
@Test
fun `invoke - affordance is visible`() = runBlockingTest {
val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
val model =
KeyguardQuickAffordanceModel.Visible(
configKey = configKey,
fun `invoke - bottom start affordance is visible`() = runBlockingTest {
val configKey = homeControls::class
homeControls.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java)
val visibleModel = latest as KeyguardQuickAffordanceModel.Visible
assertThat(visibleModel.configKey).isEqualTo(configKey)
assertThat(visibleModel.icon).isEqualTo(ICON)
assertThat(visibleModel.contentDescriptionResourceId)
.isEqualTo(CONTENT_DESCRIPTION_RESOURCE_ID)
job.cancel()
}
@Test
fun `invoke - bottom end affordance is visible`() = runBlockingTest {
val configKey = quickAccessWallet::class
quickAccessWallet.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
quickAffordanceRepository.setModel(
KeyguardQuickAffordancePosition.BOTTOM_END,
model,
)
var latest: KeyguardQuickAffordanceModel? = null
@@ -91,58 +131,13 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
}
@Test
fun `invoke - affordance not visible while dozing`() = runBlockingTest {
fun `invoke - bottom start affordance hidden while dozing`() = runBlockingTest {
repository.setDozing(true)
val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
val model =
KeyguardQuickAffordanceModel.Visible(
configKey = configKey,
homeControls.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
quickAffordanceRepository.setModel(
KeyguardQuickAffordancePosition.BOTTOM_END,
model,
)
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel()
}
@Test
fun `invoke - affordance not visible when lockscreen is not showing`() = runBlockingTest {
repository.setKeyguardShowing(false)
val configKey = HomeControlsKeyguardQuickAffordanceConfig::class
val model =
KeyguardQuickAffordanceModel.Visible(
configKey = configKey,
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
quickAffordanceRepository.setModel(
KeyguardQuickAffordancePosition.BOTTOM_END,
model,
)
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest(KeyguardQuickAffordancePosition.BOTTOM_END)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel()
}
@Test
fun `invoke - affordance is none`() = runBlockingTest {
quickAffordanceRepository.setModel(
KeyguardQuickAffordancePosition.BOTTOM_START,
KeyguardQuickAffordanceModel.Hidden,
)
var latest: KeyguardQuickAffordanceModel? = null
@@ -154,6 +149,26 @@ class ObserveKeyguardQuickAffordanceUseCaseTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `invoke - bottom start affordance hidden when lockscreen is not showing`() =
runBlockingTest {
repository.setKeyguardShowing(false)
homeControls.setState(
KeyguardQuickAffordanceConfig.State.Visible(
icon = ICON,
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
)
var latest: KeyguardQuickAffordanceModel? = null
val job =
underTest(KeyguardQuickAffordancePosition.BOTTOM_START)
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isEqualTo(KeyguardQuickAffordanceModel.Hidden)
job.cancel()
}
companion object {
private val ICON: ContainedDrawable = mock()
private const val CONTENT_DESCRIPTION_RESOURCE_ID = 1337

View File

@@ -22,22 +22,20 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.containeddrawable.ContainedDrawable
import com.android.systemui.doze.util.BurnInHelperWrapper
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceConfigs
import com.android.systemui.keyguard.data.repository.FakeKeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceConfig
import com.android.systemui.keyguard.domain.usecase.FakeLaunchKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.FakeObserveKeyguardQuickAffordanceUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveAnimateBottomAreaTransitionsUseCase
import com.android.systemui.keyguard.domain.usecase.ObserveBottomAreaAlphaUseCase
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
import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePosition
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -63,14 +61,14 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
private lateinit var underTest: KeyguardBottomAreaViewModel
private lateinit var affordanceRepository: FakeKeyguardQuickAffordanceRepository
private lateinit var repository: FakeKeyguardRepository
private lateinit var registry: FakeKeyguardQuickAffordanceRegistry
private lateinit var isDozingUseCase: ObserveIsDozingUseCase
private lateinit var isKeyguardShowingUseCase: ObserveIsKeyguardShowingUseCase
private lateinit var launchQuickAffordanceUseCase: FakeLaunchKeyguardQuickAffordanceUseCase
private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var qrCodeScannerAffordanceConfig: FakeKeyguardQuickAffordanceConfig
private lateinit var observeQuickAffordanceUseCase: FakeObserveKeyguardQuickAffordanceUseCase
@Before
fun setUp() {
@@ -78,33 +76,38 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
whenever(burnInHelperWrapper.burnInOffset(anyInt(), any()))
.thenReturn(RETURNED_BURN_IN_OFFSET)
affordanceRepository = FakeKeyguardQuickAffordanceRepository()
homeControlsQuickAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
quickAccessWalletAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
qrCodeScannerAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to
listOf(
homeControlsQuickAffordanceConfig,
),
KeyguardQuickAffordancePosition.BOTTOM_END to
listOf(
quickAccessWalletAffordanceConfig,
qrCodeScannerAffordanceConfig,
),
),
)
repository = FakeKeyguardRepository()
isDozingUseCase =
ObserveIsDozingUseCase(
repository = repository,
)
isKeyguardShowingUseCase =
ObserveIsKeyguardShowingUseCase(
repository = repository,
)
launchQuickAffordanceUseCase = FakeLaunchKeyguardQuickAffordanceUseCase()
homeControlsQuickAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
quickAccessWalletAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
qrCodeScannerAffordanceConfig = object : FakeKeyguardQuickAffordanceConfig() {}
observeQuickAffordanceUseCase = FakeObserveKeyguardQuickAffordanceUseCase()
underTest =
KeyguardBottomAreaViewModel(
observeQuickAffordanceUseCase =
ObserveKeyguardQuickAffordanceUseCase(
repository = affordanceRepository,
isDozingUseCase = isDozingUseCase,
isKeyguardShowingUseCase = isKeyguardShowingUseCase,
),
observeQuickAffordanceUseCase = observeQuickAffordanceUseCase,
onQuickAffordanceClickedUseCase =
OnKeyguardQuickAffordanceClickedUseCase(
configs =
FakeKeyguardQuickAffordanceConfigs(
registry =
FakeKeyguardQuickAffordanceRegistry(
mapOf(
KeyguardQuickAffordancePosition.BOTTOM_START to
listOf(
@@ -141,98 +144,11 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
}
@Test
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,
icon = mock(),
canShowWhileLocked = false,
intent = Intent("action"),
)
val configKey =
setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
testConfig = testConfig,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = testConfig,
configKey = configKey,
)
job.cancel()
}
@Test
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,
icon = mock(),
canShowWhileLocked = false,
intent =
null, // This will cause it to tell the system that the click was handled.
)
val configKey =
setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_END,
testConfig = config,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = config,
configKey = configKey,
)
job.cancel()
}
@Test
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)
repository.setKeyguardShowing(true)
val config =
TestConfig(
isVisible = false,
)
val configKey =
setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
testConfig = config,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = config,
configKey = configKey,
)
job.cancel()
}
@Test
fun `startButton - present - dozing - lockscreen showing - model is none`() = runBlockingTest {
fun `startButton - present - visible model - starts activity on click`() = runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this)
repository.setDozing(true)
repository.setKeyguardShowing(true)
val config =
val testConfig =
TestConfig(
isVisible = true,
icon = mock(),
@@ -242,45 +158,65 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
val configKey =
setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
testConfig = config,
testConfig = testConfig,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = TestConfig(isVisible = false),
testConfig = testConfig,
configKey = configKey,
)
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)
fun `endButton - present - visible model - do nothing on click`() = runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.endButton.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,
val config =
TestConfig(
isVisible = true,
icon = mock(),
canShowWhileLocked = false,
intent = null, // This will cause it to tell the system that the click was handled.
)
job.cancel()
}
val configKey =
setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_END,
testConfig = config,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = config,
configKey = configKey,
)
job.cancel()
}
@Test
fun `startButton - not present - model is hidden`() = runBlockingTest {
var latest: KeyguardQuickAffordanceViewModel? = null
val job = underTest.startButton.onEach { latest = it }.launchIn(this)
val config =
TestConfig(
isVisible = false,
)
val configKey =
setUpQuickAffordanceModel(
position = KeyguardQuickAffordancePosition.BOTTOM_START,
testConfig = config,
)
assertQuickAffordanceViewModel(
viewModel = latest,
testConfig = config,
configKey = configKey,
)
job.cancel()
}
@Test
fun animateButtonReveal() = runBlockingTest {
@@ -413,7 +349,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
job.cancel()
}
private fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
private suspend fun setDozeAmountAndCalculateExpectedTranslationY(dozeAmount: Float): Float {
repository.setDozeAmount(dozeAmount)
return dozeAmount * (RETURNED_BURN_IN_OFFSET - DEFAULT_BURN_IN_OFFSET)
}
@@ -428,27 +364,31 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() {
KeyguardQuickAffordancePosition.BOTTOM_END -> quickAccessWalletAffordanceConfig
}
affordanceRepository.setModel(
position = position,
model =
if (testConfig.isVisible) {
if (testConfig.intent != null) {
config.onClickedResult =
KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
intent = testConfig.intent,
canShowWhileLocked = testConfig.canShowWhileLocked,
)
}
KeyguardQuickAffordanceModel.Visible(
configKey = config::class,
icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
} else {
KeyguardQuickAffordanceModel.Hidden
val state =
if (testConfig.isVisible) {
if (testConfig.intent != null) {
config.onClickedResult =
KeyguardQuickAffordanceConfig.OnClickedResult.StartActivity(
intent = testConfig.intent,
canShowWhileLocked = testConfig.canShowWhileLocked,
)
}
KeyguardQuickAffordanceConfig.State.Visible(
icon = testConfig.icon ?: error("Icon is unexpectedly null!"),
contentDescriptionResourceId = CONTENT_DESCRIPTION_RESOURCE_ID,
)
} else {
KeyguardQuickAffordanceConfig.State.Hidden
}
config.setState(state)
val configKey = config::class
observeQuickAffordanceUseCase.setModel(
position,
KeyguardQuickAffordanceModel.from(state, configKey)
)
return config::class
return configKey
}
private fun assertQuickAffordanceViewModel(