Skip to content

Commit 6397d15

Browse files
authored
Port XR compose snippets from DAC (#500)
* Port compose snippets from DAC * Migrate snippets from "Bring your Android app into 3D with XR" * Migrate snippets from "Develop UI for Android Views-based Apps"
1 parent 824636b commit 6397d15

14 files changed

+903
-0
lines changed

xr/build.gradle.kts

+32
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
plugins {
22
alias(libs.plugins.android.application)
33
alias(libs.plugins.kotlin.android)
4+
alias(libs.plugins.compose.compiler)
45
}
56

67
android {
@@ -21,14 +22,45 @@ android {
2122
kotlinOptions {
2223
jvmTarget = "11"
2324
}
25+
buildFeatures {
26+
compose = true
27+
}
2428
}
2529

2630
dependencies {
2731
implementation(libs.androidx.xr.arcore)
2832
implementation(libs.androidx.xr.scenecore)
2933
implementation(libs.androidx.xr.compose)
34+
3035
implementation(libs.androidx.activity.ktx)
3136
implementation(libs.guava)
3237
implementation(libs.kotlinx.coroutines.guava)
3338

39+
val composeBom = platform(libs.androidx.compose.bom)
40+
implementation(composeBom)
41+
42+
implementation(libs.androidx.compose.ui)
43+
implementation(libs.androidx.compose.ui.util)
44+
implementation(libs.androidx.compose.ui.graphics)
45+
implementation(libs.androidx.graphics.shapes)
46+
implementation(libs.androidx.compose.ui.tooling.preview)
47+
implementation(libs.androidx.compose.ui.viewbinding)
48+
implementation(libs.androidx.paging.compose)
49+
implementation(libs.androidx.compose.animation.graphics)
50+
51+
implementation(libs.androidx.compose.material3)
52+
implementation(libs.androidx.compose.material3.adaptive)
53+
implementation(libs.androidx.compose.material3.adaptive.layout)
54+
implementation(libs.androidx.compose.material3.adaptive.navigation)
55+
implementation(libs.androidx.compose.material3.adaptive.navigation.suite)
56+
implementation(libs.androidx.compose.material)
57+
58+
implementation(libs.androidx.compose.runtime)
59+
implementation(libs.androidx.compose.runtime.livedata)
60+
implementation(libs.androidx.compose.material.iconsExtended)
61+
implementation(libs.androidx.compose.material.ripple)
62+
implementation(libs.androidx.constraintlayout.compose)
63+
64+
implementation(libs.androidx.activity.compose)
65+
implementation(libs.androidx.appcompat)
3466
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Copyright 2025 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+
* https://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.example.xr.compose
18+
19+
import androidx.compose.foundation.background
20+
import androidx.compose.foundation.layout.Arrangement
21+
import androidx.compose.foundation.layout.Box
22+
import androidx.compose.foundation.layout.Row
23+
import androidx.compose.foundation.layout.height
24+
import androidx.compose.foundation.layout.padding
25+
import androidx.compose.foundation.layout.width
26+
import androidx.compose.foundation.shape.CircleShape
27+
import androidx.compose.foundation.shape.CornerSize
28+
import androidx.compose.material.MaterialTheme
29+
import androidx.compose.material.Surface
30+
import androidx.compose.material3.Text
31+
import androidx.compose.runtime.Composable
32+
import androidx.compose.ui.Alignment
33+
import androidx.compose.ui.Modifier
34+
import androidx.compose.ui.draw.clip
35+
import androidx.compose.ui.graphics.Color
36+
import androidx.compose.ui.res.dimensionResource
37+
import androidx.compose.ui.unit.dp
38+
import androidx.compose.ui.unit.sp
39+
import androidx.xr.compose.spatial.EdgeOffset
40+
import androidx.xr.compose.spatial.Orbiter
41+
import androidx.xr.compose.spatial.OrbiterEdge
42+
import androidx.xr.compose.spatial.Subspace
43+
import androidx.xr.compose.subspace.SpatialPanel
44+
import androidx.xr.compose.subspace.SpatialRow
45+
import androidx.xr.compose.subspace.layout.SpatialRoundedCornerShape
46+
import androidx.xr.compose.subspace.layout.SubspaceModifier
47+
import androidx.xr.compose.subspace.layout.height
48+
import androidx.xr.compose.subspace.layout.movable
49+
import androidx.xr.compose.subspace.layout.resizable
50+
import androidx.xr.compose.subspace.layout.width
51+
import com.example.xr.R
52+
53+
@Composable
54+
private fun OrbiterExampleSubspace() {
55+
// [START androidxr_compose_OrbiterExampleSubspace]
56+
Subspace {
57+
SpatialPanel(
58+
SubspaceModifier
59+
.height(824.dp)
60+
.width(1400.dp)
61+
.movable()
62+
.resizable()
63+
) {
64+
SpatialPanelContent()
65+
OrbiterExample()
66+
}
67+
}
68+
// [END androidxr_compose_OrbiterExampleSubspace]
69+
}
70+
71+
// [START androidxr_compose_OrbiterExample]
72+
@Composable
73+
fun OrbiterExample() {
74+
Orbiter(
75+
position = OrbiterEdge.Bottom,
76+
offset = 96.dp,
77+
alignment = Alignment.CenterHorizontally
78+
) {
79+
Surface(Modifier.clip(CircleShape)) {
80+
Row(
81+
Modifier
82+
.background(color = Color.Black)
83+
.height(100.dp)
84+
.width(600.dp),
85+
horizontalArrangement = Arrangement.Center,
86+
verticalAlignment = Alignment.CenterVertically
87+
) {
88+
Text(
89+
text = "Orbiter",
90+
color = Color.White,
91+
fontSize = 50.sp
92+
)
93+
}
94+
}
95+
}
96+
}
97+
// [END androidxr_compose_OrbiterExample]
98+
99+
@Composable
100+
fun OrbiterAnchoringExample() {
101+
// [START androidxr_compose_OrbiterAnchoringExample]
102+
Subspace {
103+
SpatialRow {
104+
Orbiter(
105+
position = OrbiterEdge.Top,
106+
offset = EdgeOffset.inner(8.dp),
107+
shape = SpatialRoundedCornerShape(size = CornerSize(50))
108+
) {
109+
Text(
110+
"Hello World!",
111+
style = MaterialTheme.typography.h2,
112+
modifier = Modifier
113+
.background(Color.White)
114+
.padding(16.dp)
115+
)
116+
}
117+
SpatialPanel(
118+
SubspaceModifier
119+
.height(824.dp)
120+
.width(1400.dp)
121+
) {
122+
Box(
123+
modifier = Modifier
124+
.background(Color.Red)
125+
)
126+
}
127+
SpatialPanel(
128+
SubspaceModifier
129+
.height(824.dp)
130+
.width(1400.dp)
131+
) {
132+
Box(
133+
modifier = Modifier
134+
.background(Color.Blue)
135+
)
136+
}
137+
}
138+
}
139+
// [END androidxr_compose_OrbiterAnchoringExample]
140+
}
141+
142+
@Composable
143+
private fun NavigationRail() {}
144+
145+
@Composable
146+
private fun Ui2DToOribiter() {
147+
// [START androidxr_compose_orbiter_comparison]
148+
// Previous approach
149+
NavigationRail()
150+
151+
// New XR differentiated approach
152+
Orbiter(
153+
position = OrbiterEdge.Start,
154+
offset = dimensionResource(R.dimen.start_orbiter_padding),
155+
alignment = Alignment.Top
156+
) {
157+
NavigationRail()
158+
}
159+
// [END androidxr_compose_orbiter_comparison]
160+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2025 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+
* https://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.example.xr.compose
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.xr.compose.platform.LocalSpatialCapabilities
21+
22+
@Composable
23+
private fun SupportingInfoPanel() {}
24+
25+
@Composable
26+
private fun ButtonToPresentInfoModal() {}
27+
28+
@Composable
29+
private fun SpatialCapabilitiesCheck() {
30+
// [START androidxr_compose_checkSpatialCapabilities]
31+
if (LocalSpatialCapabilities.current.isSpatialUiEnabled) {
32+
SupportingInfoPanel()
33+
} else {
34+
ButtonToPresentInfoModal()
35+
}
36+
37+
// Similar check for audio
38+
val spatialAudioEnabled = LocalSpatialCapabilities.current.isSpatialAudioEnabled
39+
// [END androidxr_compose_checkSpatialCapabilities]
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2025 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+
* https://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.example.xr.compose
18+
19+
import androidx.compose.foundation.layout.Box
20+
import androidx.compose.foundation.layout.height
21+
import androidx.compose.foundation.layout.width
22+
import androidx.compose.material3.Button
23+
import androidx.compose.material3.Text
24+
import androidx.compose.runtime.Composable
25+
import androidx.compose.runtime.LaunchedEffect
26+
import androidx.compose.runtime.getValue
27+
import androidx.compose.runtime.mutableStateOf
28+
import androidx.compose.runtime.remember
29+
import androidx.compose.runtime.setValue
30+
import androidx.compose.ui.Modifier
31+
import androidx.compose.ui.unit.dp
32+
import androidx.compose.ui.window.Dialog
33+
import androidx.xr.compose.spatial.SpatialDialog
34+
import androidx.xr.compose.spatial.SpatialDialogProperties
35+
import kotlinx.coroutines.delay
36+
37+
// [START androidxr_compose_DelayedDialog]
38+
@Composable
39+
fun DelayedDialog() {
40+
var showDialog by remember { mutableStateOf(false) }
41+
LaunchedEffect(Unit) {
42+
delay(3000)
43+
showDialog = true
44+
}
45+
if (showDialog) {
46+
SpatialDialog(
47+
onDismissRequest = { showDialog = false },
48+
SpatialDialogProperties(
49+
dismissOnBackPress = true
50+
)
51+
) {
52+
Box(
53+
Modifier
54+
.height(150.dp)
55+
.width(150.dp)
56+
) {
57+
Button(onClick = { showDialog = false }) {
58+
Text("OK")
59+
}
60+
}
61+
}
62+
}
63+
}
64+
// [END androidxr_compose_DelayedDialog]
65+
66+
@Composable
67+
private fun MyDialogContent() {}
68+
@Composable
69+
private fun SpatialDialogComparison() {
70+
val onDismissRequest: () -> Unit = {}
71+
// [START androidxr_compose_spatialdialog_comparison]
72+
// Previous approach
73+
Dialog(
74+
onDismissRequest = onDismissRequest
75+
) {
76+
MyDialogContent()
77+
}
78+
79+
// New XR differentiated approach
80+
SpatialDialog(
81+
onDismissRequest = onDismissRequest
82+
) {
83+
MyDialogContent()
84+
}
85+
// [END androidxr_compose_spatialdialog_comparison]
86+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2025 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+
* https://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.example.xr.compose
18+
19+
import androidx.compose.runtime.Composable
20+
import androidx.xr.compose.spatial.SpatialElevation
21+
import androidx.xr.compose.spatial.SpatialElevationLevel
22+
23+
@Composable
24+
private fun ComposableThatShouldElevateInXr() {}
25+
26+
@Composable
27+
private fun SpatialElevationExample() {
28+
// [START androidxr_compose_spatialelevation]
29+
// Elevate an otherwise 2D Composable (signified here by ComposableThatShouldElevateInXr).
30+
SpatialElevation(spatialElevationLevel = SpatialElevationLevel.Level4) {
31+
ComposableThatShouldElevateInXr()
32+
}
33+
// [END androidxr_compose_spatialelevation]
34+
}

0 commit comments

Comments
 (0)