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