1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 @file:OptIn(ExperimentalCoroutinesApi::class)
18 
19 package com.android.systemui.scene.shared.model
20 
21 import com.android.compose.animation.scene.SceneKey
22 import com.android.compose.animation.scene.TransitionKey
23 import kotlinx.coroutines.CoroutineScope
24 import kotlinx.coroutines.ExperimentalCoroutinesApi
25 import kotlinx.coroutines.flow.MutableStateFlow
26 import kotlinx.coroutines.flow.SharingStarted
27 import kotlinx.coroutines.flow.StateFlow
28 import kotlinx.coroutines.flow.asStateFlow
29 import kotlinx.coroutines.flow.flatMapLatest
30 import kotlinx.coroutines.flow.stateIn
31 
32 /**
33  * Delegates calls to a runtime-provided [SceneDataSource] or to a no-op implementation if a
34  * delegate isn't set.
35  */
36 class SceneDataSourceDelegator(
37     applicationScope: CoroutineScope,
38     config: SceneContainerConfig,
39 ) : SceneDataSource {
40     private val noOpDelegate = NoOpSceneDataSource(config.initialSceneKey)
41     private val delegateMutable = MutableStateFlow<SceneDataSource>(noOpDelegate)
42 
43     override val currentScene: StateFlow<SceneKey> =
44         delegateMutable
45             .flatMapLatest { delegate -> delegate.currentScene }
46             .stateIn(
47                 scope = applicationScope,
48                 started = SharingStarted.WhileSubscribed(),
49                 initialValue = config.initialSceneKey,
50             )
51 
52     override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
53         delegateMutable.value.changeScene(
54             toScene = toScene,
55             transitionKey = transitionKey,
56         )
57     }
58 
59     override fun snapToScene(toScene: SceneKey) {
60         delegateMutable.value.snapToScene(
61             toScene = toScene,
62         )
63     }
64 
65     /**
66      * Binds the current, dependency injection provided [SceneDataSource] to the given object.
67      *
68      * In other words: once this is invoked, the state and functionality of the [SceneDataSource]
69      * will be served by the given [delegate].
70      *
71      * If `null` is passed in, the delegator will use a no-op implementation of [SceneDataSource].
72      *
73      * This removes any previously set delegate.
74      */
75     fun setDelegate(delegate: SceneDataSource?) {
76         delegateMutable.value = delegate ?: noOpDelegate
77     }
78 
79     private class NoOpSceneDataSource(
80         initialSceneKey: SceneKey,
81     ) : SceneDataSource {
82         override val currentScene: StateFlow<SceneKey> =
83             MutableStateFlow(initialSceneKey).asStateFlow()
84 
85         override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = Unit
86 
87         override fun snapToScene(toScene: SceneKey) = Unit
88     }
89 }
90