1 /*
2  * Copyright 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 androidx.xr.compose.platform
18 
19 import android.util.CloseGuard
20 import androidx.annotation.RestrictTo
21 import androidx.xr.compose.subspace.node.SubspaceSemanticsInfo
22 
23 /**
24  * Manager for all [SpatialComposeScene]s that are created when the [SceneManager] is running.
25  *
26  * Enables finding all semantic roots in a spatial scene graph. This is useful for testing libraries
27  * as well as developer tooling to help semantically identify parts of the compose tree. It is not
28  * intended to be used in individual apps.
29  */
30 @Suppress("NotCloseable")
31 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
32 public object SceneManager : AutoCloseable {
33     private val registeredScenes: MutableList<SpatialComposeScene> = mutableListOf()
34     private var isRunning = false
35     private val guard = CloseGuard()
36 
37     /**
38      * Start keeping track of the scenes that are created. Scenes created before [SceneManager] is
39      * running will not be tracked.
40      */
startnull41     public fun start() {
42         isRunning = true
43         guard.open("stop")
44     }
45 
46     /**
47      * Stop tracking the created scenes and clear the set of scenes that [SceneManager] was keeping
48      * track of.
49      */
stopnull50     public fun stop() {
51         guard.close()
52         isRunning = false
53         registeredScenes.clear()
54     }
55 
56     /** Alias to [SceneManager.stop] To implement the [AutoCloseable] interface. */
closenull57     override fun close() {
58         stop()
59     }
60 
onSceneCreatednull61     internal fun onSceneCreated(scene: SpatialComposeScene) {
62         if (isRunning) {
63             registeredScenes.add(scene)
64         }
65     }
66 
onSceneDisposednull67     internal fun onSceneDisposed(scene: SpatialComposeScene) {
68         if (isRunning) {
69             registeredScenes.remove(scene)
70         }
71     }
72 
73     /**
74      * Returns all root subspace semantics nodes of all registered scenes.
75      *
76      * [SceneManager.start] should be called before attempting to get the root subspace semantics
77      * nodes. This will throw an [IllegalStateException] if the [SceneManager] is not in a running
78      * state.
79      */
getAllRootSubspaceSemanticsNodesnull80     public fun getAllRootSubspaceSemanticsNodes(): List<SubspaceSemanticsInfo> {
81         check(isRunning) { "SceneManager is not started. Call SceneManager.start() first." }
82         return registeredScenes.map { it.rootElement.compositionOwner.root.measurableLayout }
83     }
84 
getSceneCountnull85     public fun getSceneCount(): Int {
86         return registeredScenes.size
87     }
88 }
89