Merge changes from topic "flexiglass-container-config-b279501596" into udc-dev
* changes: [flexiglass] Container config - UI layer. [flexiglass] Container config - domain layer. [flexiglass] Container config - data layer.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.data.model
|
||||
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
|
||||
/** Models the configuration of a single scene container. */
|
||||
data class SceneContainerConfig(
|
||||
/** Container name. Must be unique across all containers in System UI. */
|
||||
val name: String,
|
||||
|
||||
/**
|
||||
* The keys to all scenes in the container, sorted by z-order such that the last one renders on
|
||||
* top of all previous ones. Scene keys within the same container must not repeat but it's okay
|
||||
* to have the same scene keys in different containers.
|
||||
*/
|
||||
val sceneKeys: List<SceneKey>,
|
||||
|
||||
/**
|
||||
* The key of the scene that is the initial current scene when the container is first set up,
|
||||
* before taking any application state in to account.
|
||||
*/
|
||||
val initialSceneKey: SceneKey,
|
||||
) {
|
||||
init {
|
||||
check(sceneKeys.isNotEmpty()) { "A container must have at least one scene key." }
|
||||
|
||||
check(sceneKeys.contains(initialSceneKey)) {
|
||||
"The initial key \"$initialSceneKey\" is not present in this container."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.data.repository
|
||||
|
||||
import com.android.systemui.scene.data.model.SceneContainerConfig
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/** Source of truth for scene framework application state. */
|
||||
class SceneContainerRepository
|
||||
@Inject
|
||||
constructor(
|
||||
containerConfigurations: Set<SceneContainerConfig>,
|
||||
) {
|
||||
|
||||
private val containerConfigByName: Map<String, SceneContainerConfig> =
|
||||
containerConfigurations.associateBy { config -> config.name }
|
||||
private val containerVisibilityByName: Map<String, MutableStateFlow<Boolean>> =
|
||||
containerConfigByName
|
||||
.map { (containerName, _) -> containerName to MutableStateFlow(true) }
|
||||
.toMap()
|
||||
private val currentSceneByContainerName: Map<String, MutableStateFlow<SceneModel>> =
|
||||
containerConfigByName
|
||||
.map { (containerName, config) ->
|
||||
containerName to MutableStateFlow(SceneModel(config.initialSceneKey))
|
||||
}
|
||||
.toMap()
|
||||
private val sceneTransitionProgressByContainerName: Map<String, MutableStateFlow<Float>> =
|
||||
containerConfigByName
|
||||
.map { (containerName, _) -> containerName to MutableStateFlow(1f) }
|
||||
.toMap()
|
||||
|
||||
init {
|
||||
val repeatedContainerNames =
|
||||
containerConfigurations
|
||||
.groupingBy { config -> config.name }
|
||||
.eachCount()
|
||||
.filter { (_, count) -> count > 1 }
|
||||
check(repeatedContainerNames.isEmpty()) {
|
||||
"Container names must be unique. The following container names appear more than once: ${
|
||||
repeatedContainerNames
|
||||
.map { (name, count) -> "\"$name\" appears $count times" }
|
||||
.joinToString(", ")
|
||||
}"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the keys to all scenes in the container with the given name.
|
||||
*
|
||||
* The scenes will be sorted in z-order such that the last one is the one that should be
|
||||
* rendered on top of all previous ones.
|
||||
*/
|
||||
fun allSceneKeys(containerName: String): List<SceneKey> {
|
||||
return containerConfigByName[containerName]?.sceneKeys
|
||||
?: error(noSuchContainerErrorMessage(containerName))
|
||||
}
|
||||
|
||||
/** Sets the current scene in the container with the given name. */
|
||||
fun setCurrentScene(containerName: String, scene: SceneModel) {
|
||||
check(allSceneKeys(containerName).contains(scene.key)) {
|
||||
"""
|
||||
Cannot set current scene key to "${scene.key}". The container "$containerName" does
|
||||
not contain a scene with that key.
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
|
||||
currentSceneByContainerName.setValue(containerName, scene)
|
||||
}
|
||||
|
||||
/** The current scene in the container with the given name. */
|
||||
fun currentScene(containerName: String): StateFlow<SceneModel> {
|
||||
return currentSceneByContainerName.mutableOrError(containerName).asStateFlow()
|
||||
}
|
||||
|
||||
/** Sets whether the container with the given name is visible. */
|
||||
fun setVisible(containerName: String, isVisible: Boolean) {
|
||||
containerVisibilityByName.setValue(containerName, isVisible)
|
||||
}
|
||||
|
||||
/** Whether the container with the given name should be visible. */
|
||||
fun isVisible(containerName: String): StateFlow<Boolean> {
|
||||
return containerVisibilityByName.mutableOrError(containerName).asStateFlow()
|
||||
}
|
||||
|
||||
/** Sets scene transition progress to the current scene in the container with the given name. */
|
||||
fun setSceneTransitionProgress(containerName: String, progress: Float) {
|
||||
sceneTransitionProgressByContainerName.setValue(containerName, progress)
|
||||
}
|
||||
|
||||
/** Progress of the transition into the current scene in the container with the given name. */
|
||||
fun sceneTransitionProgress(containerName: String): StateFlow<Float> {
|
||||
return sceneTransitionProgressByContainerName.mutableOrError(containerName).asStateFlow()
|
||||
}
|
||||
|
||||
private fun <T> Map<String, MutableStateFlow<T>>.mutableOrError(
|
||||
containerName: String,
|
||||
): MutableStateFlow<T> {
|
||||
return this[containerName] ?: error(noSuchContainerErrorMessage(containerName))
|
||||
}
|
||||
|
||||
private fun <T> Map<String, MutableStateFlow<T>>.setValue(
|
||||
containerName: String,
|
||||
value: T,
|
||||
) {
|
||||
val mutable = mutableOrError(containerName)
|
||||
mutable.value = value
|
||||
}
|
||||
|
||||
private fun noSuchContainerErrorMessage(containerName: String): String {
|
||||
return """
|
||||
No container named "$containerName". Existing containers:
|
||||
${containerConfigByName.values.joinToString(", ") { it.name }}
|
||||
"""
|
||||
.trimIndent()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.domain.interactor
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.scene.data.repository.SceneContainerRepository
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/** Business logic and app state accessors for the scene framework. */
|
||||
@SysUISingleton
|
||||
class SceneInteractor
|
||||
@Inject
|
||||
constructor(
|
||||
private val repository: SceneContainerRepository,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Returns the keys of all scenes in the container with the given name.
|
||||
*
|
||||
* The scenes will be sorted in z-order such that the last one is the one that should be
|
||||
* rendered on top of all previous ones.
|
||||
*/
|
||||
fun allSceneKeys(containerName: String): List<SceneKey> {
|
||||
return repository.allSceneKeys(containerName)
|
||||
}
|
||||
|
||||
/** Sets the scene in the container with the given name. */
|
||||
fun setCurrentScene(containerName: String, scene: SceneModel) {
|
||||
repository.setCurrentScene(containerName, scene)
|
||||
}
|
||||
|
||||
/** The current scene in the container with the given name. */
|
||||
fun currentScene(containerName: String): StateFlow<SceneModel> {
|
||||
return repository.currentScene(containerName)
|
||||
}
|
||||
|
||||
/** Sets the visibility of the container with the given name. */
|
||||
fun setVisible(containerName: String, isVisible: Boolean) {
|
||||
return repository.setVisible(containerName, isVisible)
|
||||
}
|
||||
|
||||
/** Whether the container with the given name is visible. */
|
||||
fun isVisible(containerName: String): StateFlow<Boolean> {
|
||||
return repository.isVisible(containerName)
|
||||
}
|
||||
|
||||
/** Sets scene transition progress to the current scene in the container with the given name. */
|
||||
fun setSceneTransitionProgress(containerName: String, progress: Float) {
|
||||
repository.setSceneTransitionProgress(containerName, progress)
|
||||
}
|
||||
|
||||
/** Progress of the transition into the current scene in the container with the given name. */
|
||||
fun sceneTransitionProgress(containerName: String): StateFlow<Float> {
|
||||
return repository.sceneTransitionProgress(containerName)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.shared.model
|
||||
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
/**
|
||||
* Defines interface for classes that can describe a "scene".
|
||||
*
|
||||
* In the scene framework, there can be multiple scenes in a single scene "container". The container
|
||||
* takes care of rendering the current scene and allowing scenes to be switched from one to another
|
||||
* based on either user action (for example, swiping down while on the lock screen scene may switch
|
||||
* to the shade scene).
|
||||
*
|
||||
* The framework also supports multiple containers, each one with its own configuration.
|
||||
*/
|
||||
interface Scene {
|
||||
|
||||
/** Uniquely-identifying key for this scene. The key must be unique within its container. */
|
||||
val key: SceneKey
|
||||
|
||||
/**
|
||||
* Returns a mapping between [UserAction] and flows that emit a [SceneModel].
|
||||
*
|
||||
* When the scene framework detects the user action, it starts a transition to the scene
|
||||
* described by the latest value in the flow that's mapped from that user action.
|
||||
*
|
||||
* Once the [Scene] becomes the current one, the scene framework will invoke this method and set
|
||||
* up collectors to watch for new values emitted to each of the flows. If a value is added to
|
||||
* the map at a given [UserAction], the framework will set up user input handling for that
|
||||
* [UserAction] and, if such a user action is detected, the framework will initiate a transition
|
||||
* to that [SceneModel].
|
||||
*
|
||||
* Note that calling this method does _not_ mean that the given user action has occurred.
|
||||
* Instead, the method is called before any user action/gesture is detected so that the
|
||||
* framework can decide whether to set up gesture/input detectors/listeners for that type of
|
||||
* user action.
|
||||
*
|
||||
* Note that a missing value for a specific [UserAction] means that the user action of the given
|
||||
* type is not currently active in the scene and should be ignored by the framework, while the
|
||||
* current scene is this one.
|
||||
*
|
||||
* The API is designed such that it's possible to emit ever-changing values for each
|
||||
* [UserAction] to enable, disable, or change the destination scene of a given user action.
|
||||
*/
|
||||
fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
|
||||
MutableStateFlow(emptyMap<UserAction, SceneModel>()).asStateFlow()
|
||||
}
|
||||
|
||||
/** Enumerates all scene framework supported user actions. */
|
||||
sealed interface UserAction {
|
||||
|
||||
/** The user is scrolling, dragging, swiping, or flinging. */
|
||||
data class Swipe(
|
||||
/** The direction of the swipe. */
|
||||
val direction: Direction,
|
||||
/** The number of pointers that were used (for example, one or two fingers). */
|
||||
val pointerCount: Int = 1,
|
||||
) : UserAction
|
||||
|
||||
/** The user has hit the back button or performed the back navigation gesture. */
|
||||
object Back : UserAction
|
||||
}
|
||||
|
||||
/** Enumerates all known "cardinal" directions for user actions. */
|
||||
enum class Direction {
|
||||
LEFT,
|
||||
UP,
|
||||
RIGHT,
|
||||
DOWN,
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.shared.model
|
||||
|
||||
/** Keys of all known scenes. */
|
||||
sealed class SceneKey(
|
||||
private val loggingName: String,
|
||||
) {
|
||||
/**
|
||||
* The bouncer is the scene that displays authentication challenges like PIN, password, or
|
||||
* pattern.
|
||||
*/
|
||||
object Bouncer : SceneKey("bouncer")
|
||||
|
||||
/**
|
||||
* "Gone" is not a real scene but rather the absence of scenes when we want to skip showing any
|
||||
* content from the scene framework.
|
||||
*/
|
||||
object Gone : SceneKey("gone")
|
||||
|
||||
/** The lock screen is the scene that shows when the device is locked. */
|
||||
object LockScreen : SceneKey("lockscreen")
|
||||
|
||||
/**
|
||||
* The shade is the scene whose primary purpose is to show a scrollable list of notifications.
|
||||
*/
|
||||
object Shade : SceneKey("shade")
|
||||
|
||||
/** The quick settings scene shows the quick setting tiles. */
|
||||
object QuickSettings : SceneKey("quick_settings")
|
||||
|
||||
override fun toString(): String {
|
||||
return loggingName
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.shared.model
|
||||
|
||||
/** Models a scene. */
|
||||
data class SceneModel(
|
||||
|
||||
/** The key of the scene. */
|
||||
val key: SceneKey,
|
||||
|
||||
/** An optional name for the transition that led to this scene being the current scene. */
|
||||
val transitionName: String? = null,
|
||||
)
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.ui.viewmodel
|
||||
|
||||
import com.android.systemui.scene.domain.interactor.SceneInteractor
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
/** Models UI state for a single scene container. */
|
||||
class SceneContainerViewModel
|
||||
@AssistedInject
|
||||
constructor(
|
||||
private val interactor: SceneInteractor,
|
||||
@Assisted private val containerName: String,
|
||||
) {
|
||||
/**
|
||||
* Keys of all scenes in the container.
|
||||
*
|
||||
* The scenes will be sorted in z-order such that the last one is the one that should be
|
||||
* rendered on top of all previous ones.
|
||||
*/
|
||||
val allSceneKeys: List<SceneKey> = interactor.allSceneKeys(containerName)
|
||||
|
||||
/** The current scene. */
|
||||
val currentScene: StateFlow<SceneModel> = interactor.currentScene(containerName)
|
||||
|
||||
/** Whether the container is visible. */
|
||||
val isVisible: StateFlow<Boolean> = interactor.isVisible(containerName)
|
||||
|
||||
/** Requests a transition to the scene with the given key. */
|
||||
fun setCurrentScene(scene: SceneModel) {
|
||||
interactor.setCurrentScene(containerName, scene)
|
||||
}
|
||||
|
||||
/** Notifies of the progress of a scene transition. */
|
||||
fun setSceneTransitionProgress(progress: Float) {
|
||||
interactor.setSceneTransitionProgress(containerName, progress)
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(
|
||||
containerName: String,
|
||||
): SceneContainerViewModel
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.scene.data.repository
|
||||
|
||||
import com.android.systemui.scene.data.model.SceneContainerConfig
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
|
||||
fun fakeSceneContainerRepository(
|
||||
containerConfigurations: Set<SceneContainerConfig> =
|
||||
setOf(
|
||||
fakeSceneContainerConfig("container1"),
|
||||
fakeSceneContainerConfig("container2"),
|
||||
)
|
||||
): SceneContainerRepository {
|
||||
return SceneContainerRepository(containerConfigurations)
|
||||
}
|
||||
|
||||
fun fakeSceneKeys(): List<SceneKey> {
|
||||
return listOf(
|
||||
SceneKey.QuickSettings,
|
||||
SceneKey.Shade,
|
||||
SceneKey.LockScreen,
|
||||
SceneKey.Bouncer,
|
||||
SceneKey.Gone,
|
||||
)
|
||||
}
|
||||
|
||||
fun fakeSceneContainerConfig(
|
||||
name: String,
|
||||
sceneKeys: List<SceneKey> = fakeSceneKeys(),
|
||||
): SceneContainerConfig {
|
||||
return SceneContainerConfig(
|
||||
name = name,
|
||||
sceneKeys = sceneKeys,
|
||||
initialSceneKey = SceneKey.LockScreen,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package com.android.systemui.scene.data.repository
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class SceneContainerRepositoryTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun allSceneKeys() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
assertThat(underTest.allSceneKeys("container1"))
|
||||
.isEqualTo(
|
||||
listOf(
|
||||
SceneKey.QuickSettings,
|
||||
SceneKey.Shade,
|
||||
SceneKey.LockScreen,
|
||||
SceneKey.Bouncer,
|
||||
SceneKey.Gone,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun allSceneKeys_noSuchContainer_throws() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
underTest.allSceneKeys("nonExistingContainer")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun currentScene() = runTest {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
val currentScene by collectLastValue(underTest.currentScene("container1"))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.LockScreen))
|
||||
|
||||
underTest.setCurrentScene("container1", SceneModel(SceneKey.Shade))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun currentScene_noSuchContainer_throws() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
underTest.currentScene("nonExistingContainer")
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun setCurrentScene_noSuchContainer_throws() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
underTest.setCurrentScene("nonExistingContainer", SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun setCurrentScene_noSuchSceneInContainer_throws() {
|
||||
val underTest =
|
||||
fakeSceneContainerRepository(
|
||||
setOf(
|
||||
fakeSceneContainerConfig("container1"),
|
||||
fakeSceneContainerConfig(
|
||||
"container2",
|
||||
listOf(SceneKey.QuickSettings, SceneKey.LockScreen)
|
||||
),
|
||||
)
|
||||
)
|
||||
underTest.setCurrentScene("container2", SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible() = runTest {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
val isVisible by collectLastValue(underTest.isVisible("container1"))
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.setVisible("container1", false)
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
underTest.setVisible("container1", true)
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun isVisible_noSuchContainer_throws() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
underTest.isVisible("nonExistingContainer")
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun setVisible_noSuchContainer_throws() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
underTest.setVisible("nonExistingContainer", false)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sceneTransitionProgress() = runTest {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
val sceneTransitionProgress by
|
||||
collectLastValue(underTest.sceneTransitionProgress("container1"))
|
||||
assertThat(sceneTransitionProgress).isEqualTo(1f)
|
||||
|
||||
underTest.setSceneTransitionProgress("container1", 0.1f)
|
||||
assertThat(sceneTransitionProgress).isEqualTo(0.1f)
|
||||
|
||||
underTest.setSceneTransitionProgress("container1", 0.9f)
|
||||
assertThat(sceneTransitionProgress).isEqualTo(0.9f)
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException::class)
|
||||
fun sceneTransitionProgress_noSuchContainer_throws() {
|
||||
val underTest = fakeSceneContainerRepository()
|
||||
underTest.sceneTransitionProgress("nonExistingContainer")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class)
|
||||
|
||||
package com.android.systemui.scene.domain.interactor
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.scene.data.repository.fakeSceneContainerRepository
|
||||
import com.android.systemui.scene.data.repository.fakeSceneKeys
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class SceneInteractorTest : SysuiTestCase() {
|
||||
|
||||
private val underTest =
|
||||
SceneInteractor(
|
||||
repository = fakeSceneContainerRepository(),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun allSceneKeys() {
|
||||
assertThat(underTest.allSceneKeys("container1")).isEqualTo(fakeSceneKeys())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sceneTransitions() = runTest {
|
||||
val currentScene by collectLastValue(underTest.currentScene("container1"))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.LockScreen))
|
||||
|
||||
underTest.setCurrentScene("container1", SceneModel(SceneKey.Shade))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sceneTransitionProgress() = runTest {
|
||||
val progress by collectLastValue(underTest.sceneTransitionProgress("container1"))
|
||||
assertThat(progress).isEqualTo(1f)
|
||||
|
||||
underTest.setSceneTransitionProgress("container1", 0.55f)
|
||||
assertThat(progress).isEqualTo(0.55f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun isVisible() = runTest {
|
||||
val isVisible by collectLastValue(underTest.isVisible("container1"))
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
underTest.setVisible("container1", false)
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
underTest.setVisible("container1", true)
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package com.android.systemui.scene.ui.viewmodel
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.coroutines.collectLastValue
|
||||
import com.android.systemui.scene.data.repository.fakeSceneContainerRepository
|
||||
import com.android.systemui.scene.data.repository.fakeSceneKeys
|
||||
import com.android.systemui.scene.domain.interactor.SceneInteractor
|
||||
import com.android.systemui.scene.shared.model.SceneKey
|
||||
import com.android.systemui.scene.shared.model.SceneModel
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.JUnit4
|
||||
|
||||
@SmallTest
|
||||
@RunWith(JUnit4::class)
|
||||
class SceneContainerViewModelTest : SysuiTestCase() {
|
||||
private val interactor =
|
||||
SceneInteractor(
|
||||
repository = fakeSceneContainerRepository(),
|
||||
)
|
||||
private val underTest =
|
||||
SceneContainerViewModel(
|
||||
interactor = interactor,
|
||||
containerName = "container1",
|
||||
)
|
||||
|
||||
@Test
|
||||
fun isVisible() = runTest {
|
||||
val isVisible by collectLastValue(underTest.isVisible)
|
||||
assertThat(isVisible).isTrue()
|
||||
|
||||
interactor.setVisible("container1", false)
|
||||
assertThat(isVisible).isFalse()
|
||||
|
||||
interactor.setVisible("container1", true)
|
||||
assertThat(isVisible).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun allSceneKeys() {
|
||||
assertThat(underTest.allSceneKeys).isEqualTo(fakeSceneKeys())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sceneTransition() = runTest {
|
||||
val currentScene by collectLastValue(underTest.currentScene)
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.LockScreen))
|
||||
|
||||
underTest.setCurrentScene(SceneModel(SceneKey.Shade))
|
||||
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user