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