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.subspace
18 
19 import androidx.compose.runtime.Composable
20 import androidx.compose.runtime.DisallowComposableCalls
21 import androidx.compose.runtime.LaunchedEffect
22 import androidx.compose.runtime.remember
23 import androidx.compose.ui.platform.LocalDensity
24 import androidx.xr.compose.platform.LocalSession
25 import androidx.xr.compose.platform.disposableValueOf
26 import androidx.xr.compose.platform.getValue
27 import androidx.xr.compose.subspace.layout.CoreContentlessEntity
28 import androidx.xr.compose.subspace.layout.CoreMainPanelEntity
29 import androidx.xr.compose.subspace.layout.CorePanelEntity
30 import androidx.xr.compose.subspace.layout.CoreSurfaceEntity
31 import androidx.xr.compose.subspace.layout.SpatialShape
32 import androidx.xr.runtime.Session
33 import androidx.xr.scenecore.Entity
34 import androidx.xr.scenecore.PanelEntity
35 import androidx.xr.scenecore.SurfaceEntity
36 
37 /**
38  * Creates a [CoreContentlessEntity] that is automatically disposed of when it leaves the
39  * composition.
40  */
41 @Composable
42 @PublishedApi
rememberCoreContentlessEntitynull43 internal fun rememberCoreContentlessEntity(
44     entityFactory: @DisallowComposableCalls Session.() -> Entity
45 ): CoreContentlessEntity {
46     val session = checkNotNull(LocalSession.current) { "session must be initialized" }
47     val coreEntity by remember {
48         disposableValueOf(CoreContentlessEntity(session.entityFactory())) { it.dispose() }
49     }
50     return coreEntity
51 }
52 
53 /** Creates a [CorePanelEntity] that is automatically disposed of when it leaves the composition. */
54 @Composable
rememberCorePanelEntitynull55 internal inline fun rememberCorePanelEntity(
56     shape: SpatialShape = SpatialPanelDefaults.shape,
57     crossinline entityFactory: @DisallowComposableCalls Session.() -> PanelEntity,
58 ): CorePanelEntity {
59     val session = checkNotNull(LocalSession.current) { "session must be initialized" }
60     val density = LocalDensity.current
61     val coreEntity by remember {
62         disposableValueOf(
63             CorePanelEntity(session.entityFactory(), density).also { it.shape = shape }
64         ) {
65             it.dispose()
66         }
67     }
68     LaunchedEffect(shape) { coreEntity.shape = shape }
69     return coreEntity
70 }
71 
72 /**
73  * Creates a [CoreMainPanelEntity] that is automatically disposed of when it leaves the composition.
74  */
75 @Composable
rememberCoreMainPanelEntitynull76 internal fun rememberCoreMainPanelEntity(
77     shape: SpatialShape = SpatialPanelDefaults.shape
78 ): CoreMainPanelEntity {
79     val session = checkNotNull(LocalSession.current) { "session must be initialized" }
80     val density = LocalDensity.current
81     val coreEntity by remember {
82         disposableValueOf(CoreMainPanelEntity(session, density).also { it.shape = shape }) {
83             it.dispose()
84         }
85     }
86     LaunchedEffect(shape) { coreEntity.shape = shape }
87     return coreEntity
88 }
89 
90 /**
91  * Creates a [CoreSurfaceEntity] that is automatically disposed of when it leaves the composition.
92  */
93 @Composable
rememberCoreSurfaceEntitynull94 internal inline fun rememberCoreSurfaceEntity(
95     crossinline entityFactory: @DisallowComposableCalls Session.() -> SurfaceEntity
96 ): CoreSurfaceEntity {
97     val session = checkNotNull(LocalSession.current) { "session must be initialized" }
98     val density = LocalDensity.current
99     val coreEntity by remember {
100         disposableValueOf(CoreSurfaceEntity(session.entityFactory(), density)) { it.dispose() }
101     }
102     return coreEntity
103 }
104 
105 private var entityNamePart: Int = 0
106 
107 /**
108  * Creates a unique debugging name for an [Entity].
109  *
110  * @param name A context-specific name for the [Entity].
111  */
112 @PublishedApi
entityNamenull113 internal fun entityName(name: String): String {
114     return "$name-${entityNamePart++}"
115 }
116