1 /* 2 * 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 package com.android.systemui.scene.shared.model 18 19 import com.android.compose.animation.scene.OverlayKey 20 import com.android.compose.animation.scene.SceneKey 21 import com.android.compose.animation.scene.TransitionKey 22 import com.android.systemui.kosmos.currentValue 23 import kotlinx.coroutines.flow.MutableStateFlow 24 import kotlinx.coroutines.flow.StateFlow 25 import kotlinx.coroutines.flow.asStateFlow 26 import kotlinx.coroutines.test.TestScope 27 28 class FakeSceneDataSource(initialSceneKey: SceneKey, val testScope: TestScope) : SceneDataSource { 29 30 private val _currentScene = MutableStateFlow(initialSceneKey) 31 override val currentScene: StateFlow<SceneKey> = _currentScene.asStateFlow() 32 33 private val _currentOverlays = MutableStateFlow<Set<OverlayKey>>(emptySet()) 34 override val currentOverlays: StateFlow<Set<OverlayKey>> = _currentOverlays.asStateFlow() 35 36 private var _isPaused = false 37 val isPaused <lambda>null38 get() = testScope.currentValue { _isPaused } 39 40 private var _pendingScene: SceneKey? = null 41 val pendingScene <lambda>null42 get() = testScope.currentValue { _pendingScene } 43 44 var pendingOverlays: Set<OverlayKey>? = null 45 private set 46 47 var freezeAndAnimateToCurrentStateCallCount = 0 48 changeScenenull49 override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) { 50 if (_isPaused) { 51 _pendingScene = toScene 52 } else { 53 _currentScene.value = toScene 54 } 55 } 56 snapToScenenull57 override fun snapToScene(toScene: SceneKey) { 58 changeScene(toScene) 59 } 60 showOverlaynull61 override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { 62 if (_isPaused) { 63 pendingOverlays = (pendingOverlays ?: currentOverlays.value) + overlay 64 } else { 65 _currentOverlays.value += overlay 66 } 67 } 68 hideOverlaynull69 override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) { 70 if (_isPaused) { 71 pendingOverlays = (pendingOverlays ?: currentOverlays.value) - overlay 72 } else { 73 _currentOverlays.value -= overlay 74 } 75 } 76 replaceOverlaynull77 override fun replaceOverlay(from: OverlayKey, to: OverlayKey, transitionKey: TransitionKey?) { 78 hideOverlay(from, transitionKey) 79 showOverlay(to, transitionKey) 80 } 81 instantlyShowOverlaynull82 override fun instantlyShowOverlay(overlay: OverlayKey) { 83 showOverlay(overlay) 84 } 85 instantlyHideOverlaynull86 override fun instantlyHideOverlay(overlay: OverlayKey) { 87 hideOverlay(overlay) 88 } 89 freezeAndAnimateToCurrentStatenull90 override fun freezeAndAnimateToCurrentState() { 91 freezeAndAnimateToCurrentStateCallCount++ 92 } 93 94 /** 95 * Pauses scene and overlay changes. 96 * 97 * Any following calls to [changeScene] or overlay changing functions will be conflated and the 98 * last one will be remembered. 99 */ pausenull100 fun pause() { 101 check(!_isPaused) { "Can't pause what's already paused!" } 102 103 _isPaused = true 104 } 105 106 /** 107 * Unpauses scene and overlay changes. 108 * 109 * If there were any calls to [changeScene] since [pause] was called, the latest of the bunch 110 * will be replayed. 111 * 112 * If there were any calls to show, hide or replace overlays since [pause] was called, they will 113 * all be applied at once. 114 * 115 * If [force] is `true`, there will be no check that [isPaused] is true. 116 * 117 * If [expectedScene] is provided, will assert that it's indeed the latest called. 118 * 119 * If [expectedOverlays] is provided, will assert they are indeed present. 120 */ unpausenull121 fun unpause( 122 force: Boolean = false, 123 expectedScene: SceneKey? = null, 124 expectedOverlays: Set<OverlayKey>? = null, 125 ) { 126 check(force || _isPaused) { "Can't unpause what's already not paused!" } 127 128 _isPaused = false 129 _pendingScene?.let { _currentScene.value = it } 130 _pendingScene = null 131 pendingOverlays?.let { _currentOverlays.value = it } 132 pendingOverlays = null 133 134 check(expectedScene == null || currentScene.value == expectedScene) { 135 """ 136 Unexpected scene while unpausing. 137 Expected $expectedScene but was ${currentScene.value}. 138 """ 139 .trimIndent() 140 } 141 check(expectedOverlays == null || expectedOverlays == currentOverlays.value) { 142 "Expected $expectedOverlays, but instead found overlays ${currentOverlays.value}." 143 } 144 } 145 } 146