Skip to content

Commit 0ebe8b7

Browse files
Merge branch 'main' into bot-update-deps
2 parents 8966090 + 432ff04 commit 0ebe8b7

File tree

17 files changed

+267
-138
lines changed

17 files changed

+267
-138
lines changed

gradle/libs.versions.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ okHttp = "4.12.0"
6262
playServicesWearable = "19.0.0"
6363
protolayout = "1.2.1"
6464
recyclerview = "1.4.0"
65+
# @keep
66+
androidx-xr-arcore = "1.0.0-alpha04"
67+
androidx-xr-scenecore = "1.0.0-alpha04"
68+
androidx-xr-compose = "1.0.0-alpha04"
6569
targetSdk = "34"
6670
tiles = "1.4.1"
6771
version-catalog-update = "1.0.0"
@@ -149,9 +153,9 @@ androidx-window = { module = "androidx.window:window", version.ref = "androidx-w
149153
androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" }
150154
androidx-window-java = { module = "androidx.window:window-java", version.ref = "androidx-window-java" }
151155
androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.1"
152-
androidx-xr-arcore = { module = "androidx.xr.arcore:arcore", version.ref = "androidx-xr" }
153-
androidx-xr-compose = { module = "androidx.xr.compose:compose", version.ref = "androidx-xr" }
154-
androidx-xr-scenecore = { module = "androidx.xr.scenecore:scenecore", version.ref = "androidx-xr" }
156+
androidx-xr-arcore = { module = "androidx.xr.arcore:arcore", version.ref = "androidx-xr-arcore" }
157+
androidx-xr-compose = { module = "androidx.xr.compose:compose", version.ref = "androidx-xr-compose" }
158+
androidx-xr-scenecore = { module = "androidx.xr.scenecore:scenecore", version.ref = "androidx-xr-scenecore" }
155159
appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
156160
coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" }
157161
compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" }

xr/src/main/java/com/example/xr/arcore/Anchors.kt

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,31 @@ package com.example.xr.arcore
1919
import androidx.xr.arcore.Anchor
2020
import androidx.xr.arcore.AnchorCreateSuccess
2121
import androidx.xr.arcore.Trackable
22+
import androidx.xr.runtime.Config
2223
import androidx.xr.runtime.Session
24+
import androidx.xr.runtime.SessionConfigureConfigurationNotSupported
25+
import androidx.xr.runtime.SessionConfigurePermissionsNotGranted
26+
import androidx.xr.runtime.SessionConfigureSuccess
2327
import androidx.xr.runtime.math.Pose
2428
import androidx.xr.scenecore.AnchorEntity
2529
import androidx.xr.scenecore.Entity
30+
import androidx.xr.scenecore.scene
31+
32+
@Suppress("RestrictedApi") // b/416288516 - session.config and session.configure() are incorrectly restricted
33+
fun configureAnchoring(session: Session) {
34+
// [START androidxr_arcore_anchoring_configure]
35+
val newConfig = session.config.copy(
36+
anchorPersistence = Config.AnchorPersistenceMode.Enabled,
37+
)
38+
when (val result = session.configure(newConfig)) {
39+
is SessionConfigureConfigurationNotSupported ->
40+
TODO(/* Some combinations of configurations are not valid. Handle this failure case. */)
41+
is SessionConfigurePermissionsNotGranted ->
42+
TODO(/* The required permissions in result.permissions have not been granted. */)
43+
is SessionConfigureSuccess -> TODO(/* Success! */)
44+
}
45+
// [END androidxr_arcore_anchoring_configure]
46+
}
2647

2748
private fun createAnchorAtPose(session: Session, pose: Pose) {
2849
val pose = Pose()
@@ -45,13 +66,13 @@ private fun createAnchorAtTrackable(trackable: Trackable<*>) {
4566
}
4667

4768
private fun attachEntityToAnchor(
48-
session: androidx.xr.scenecore.Session,
69+
session: Session,
4970
entity: Entity,
5071
anchor: Anchor
5172
) {
5273
// [START androidxr_arcore_entity_tracks_anchor]
5374
AnchorEntity.create(session, anchor).apply {
54-
setParent(session.activitySpace)
75+
setParent(session.scene.activitySpace)
5576
addChild(entity)
5677
}
5778
// [END androidxr_arcore_entity_tracks_anchor]

xr/src/main/java/com/example/xr/arcore/Hands.kt

Lines changed: 30 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,59 +16,39 @@
1616

1717
package com.example.xr.arcore
1818

19-
import android.annotation.SuppressLint
20-
import android.os.Bundle
2119
import androidx.activity.ComponentActivity
2220
import androidx.lifecycle.lifecycleScope
2321
import androidx.xr.arcore.Hand
24-
import androidx.xr.arcore.HandJointType
25-
import androidx.xr.compose.platform.setSubspaceContent
22+
import androidx.xr.runtime.Config
23+
import androidx.xr.runtime.HandJointType
2624
import androidx.xr.runtime.Session
25+
import androidx.xr.runtime.SessionConfigureConfigurationNotSupported
26+
import androidx.xr.runtime.SessionConfigurePermissionsNotGranted
27+
import androidx.xr.runtime.SessionConfigureSuccess
2728
import androidx.xr.runtime.math.Pose
2829
import androidx.xr.runtime.math.Quaternion
2930
import androidx.xr.runtime.math.Vector3
30-
import androidx.xr.scenecore.Entity
31-
import androidx.xr.scenecore.GltfModel
3231
import androidx.xr.scenecore.GltfModelEntity
33-
import kotlinx.coroutines.guava.await
32+
import androidx.xr.scenecore.scene
3433
import kotlinx.coroutines.launch
3534

36-
class SampleHandsActivity : ComponentActivity() {
37-
lateinit var session: Session
38-
lateinit var scenecoreSession: androidx.xr.scenecore.Session
39-
lateinit var sessionHelper: SessionLifecycleHelper
40-
41-
var palmEntity: Entity? = null
42-
var indexFingerEntity: Entity? = null
43-
44-
override fun onCreate(savedInstanceState: Bundle?) {
45-
super.onCreate(savedInstanceState)
46-
setSubspaceContent { }
47-
48-
scenecoreSession = androidx.xr.scenecore.Session.create(this@SampleHandsActivity)
49-
lifecycleScope.launch {
50-
val model = GltfModel.create(scenecoreSession, "models/saturn_rings.glb").await()
51-
palmEntity = GltfModelEntity.create(scenecoreSession, model).apply {
52-
setScale(0.3f)
53-
setHidden(true)
54-
}
55-
indexFingerEntity = GltfModelEntity.create(scenecoreSession, model).apply {
56-
setScale(0.2f)
57-
setHidden(true)
58-
}
59-
}
60-
61-
sessionHelper = SessionLifecycleHelper(
62-
onCreateCallback = { session = it },
63-
onResumeCallback = {
64-
collectHands(session)
65-
}
66-
)
67-
lifecycle.addObserver(sessionHelper)
35+
@Suppress("RestrictedApi") // b/416288516 - session.config and session.configure() are incorrectly restricted
36+
fun ComponentActivity.configureSession(session: Session) {
37+
// [START androidxr_arcore_hand_configure]
38+
val newConfig = session.config.copy(
39+
handTracking = Config.HandTrackingMode.Enabled
40+
)
41+
when (val result = session.configure(newConfig)) {
42+
is SessionConfigureConfigurationNotSupported ->
43+
TODO(/* Some combinations of configurations are not valid. Handle this failure case. */)
44+
is SessionConfigurePermissionsNotGranted ->
45+
TODO(/* The required permissions in result.permissions have not been granted. */)
46+
is SessionConfigureSuccess -> TODO(/* Success! */)
6847
}
48+
// [END androidxr_arcore_hand_configure]
6949
}
7050

71-
fun SampleHandsActivity.collectHands(session: Session) {
51+
fun ComponentActivity.collectHands(session: Session) {
7252
lifecycleScope.launch {
7353
// [START androidxr_arcore_hand_collect]
7454
Hand.left(session)?.state?.collect { handState -> // or Hand.right(session)
@@ -85,9 +65,9 @@ fun SampleHandsActivity.collectHands(session: Session) {
8565
}
8666
}
8767

88-
@SuppressLint("RestrictedApi") // HandJointType is mistakenly @Restrict: b/397415504
89-
fun SampleHandsActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) {
90-
val palmEntity = palmEntity ?: return
68+
fun ComponentActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) {
69+
val session: Session = null!!
70+
val palmEntity: GltfModelEntity = null!!
9171
// [START androidxr_arcore_hand_entityAtHandPalm]
9272
val palmPose = leftHandState.handJoints[HandJointType.PALM] ?: return
9373

@@ -96,18 +76,18 @@ fun SampleHandsActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) {
9676
palmEntity.setHidden(angle > Math.toRadians(40.0))
9777

9878
val transformedPose =
99-
scenecoreSession.perceptionSpace.transformPoseTo(
79+
session.scene.perceptionSpace.transformPoseTo(
10080
palmPose,
101-
scenecoreSession.activitySpace,
81+
session.scene.activitySpace,
10282
)
10383
val newPosition = transformedPose.translation + transformedPose.down * 0.05f
10484
palmEntity.setPose(Pose(newPosition, transformedPose.rotation))
10585
// [END androidxr_arcore_hand_entityAtHandPalm]
10686
}
10787

108-
@SuppressLint("RestrictedApi") // HandJointType is mistakenly @Restrict: b/397415504
109-
fun SampleHandsActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) {
110-
val indexFingerEntity = indexFingerEntity ?: return
88+
fun ComponentActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) {
89+
val session: Session = null!!
90+
val indexFingerEntity: GltfModelEntity = null!!
11191

11292
// [START androidxr_arcore_hand_entityAtIndexFingerTip]
11393
val tipPose = rightHandState.handJoints[HandJointType.INDEX_TIP] ?: return
@@ -117,9 +97,9 @@ fun SampleHandsActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) {
11797
indexFingerEntity.setHidden(angle > Math.toRadians(40.0))
11898

11999
val transformedPose =
120-
scenecoreSession.perceptionSpace.transformPoseTo(
100+
session.scene.perceptionSpace.transformPoseTo(
121101
tipPose,
122-
scenecoreSession.activitySpace,
102+
session.scene.activitySpace,
123103
)
124104
val position = transformedPose.translation + transformedPose.forward * 0.03f
125105
val rotation = Quaternion.fromLookTowards(transformedPose.up, Vector3.Up)

xr/src/main/java/com/example/xr/arcore/Planes.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,30 @@
1717
package com.example.xr.arcore
1818

1919
import androidx.xr.arcore.Plane
20+
import androidx.xr.runtime.Config
2021
import androidx.xr.runtime.Session
22+
import androidx.xr.runtime.SessionConfigureConfigurationNotSupported
23+
import androidx.xr.runtime.SessionConfigurePermissionsNotGranted
24+
import androidx.xr.runtime.SessionConfigureSuccess
2125
import androidx.xr.runtime.math.Pose
2226
import androidx.xr.runtime.math.Ray
27+
import androidx.xr.scenecore.scene
28+
29+
@Suppress("RestrictedApi") // b/416288516 - session.config and session.configure() are incorrectly restricted
30+
fun configurePlaneTracking(session: Session) {
31+
// [START androidxr_arcore_planetracking_configure]
32+
val newConfig = session.config.copy(
33+
planeTracking = Config.PlaneTrackingMode.HorizontalAndVertical,
34+
)
35+
when (val result = session.configure(newConfig)) {
36+
is SessionConfigureConfigurationNotSupported ->
37+
TODO(/* Some combinations of configurations are not valid. Handle this failure case. */)
38+
is SessionConfigurePermissionsNotGranted ->
39+
TODO(/* The required permissions in result.permissions have not been granted. */)
40+
is SessionConfigureSuccess -> TODO(/* Success! */)
41+
}
42+
// [END androidxr_arcore_planetracking_configure]
43+
}
2344

2445
private suspend fun subscribePlanes(session: Session) {
2546
// [START androidxr_arcore_planes_subscribe]
@@ -30,8 +51,7 @@ private suspend fun subscribePlanes(session: Session) {
3051
}
3152

3253
private fun hitTestTable(session: Session) {
33-
val scenecoreSession: androidx.xr.scenecore.Session = null!!
34-
val pose = scenecoreSession.spatialUser.head?.transformPoseTo(Pose(), scenecoreSession.perceptionSpace) ?: return
54+
val pose = session.scene.spatialUser.head?.transformPoseTo(Pose(), session.scene.perceptionSpace) ?: return
3555
val ray = Ray(pose.translation, pose.forward)
3656
// [START androidxr_arcore_hitTest]
3757
val results = androidx.xr.arcore.hitTest(session, ray)

xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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 android.content.ContentResolver
20+
import android.net.Uri
21+
import androidx.compose.runtime.Composable
22+
import androidx.compose.runtime.remember
23+
import androidx.compose.ui.platform.LocalContext
24+
import androidx.compose.ui.unit.dp
25+
import androidx.media3.common.MediaItem
26+
import androidx.media3.exoplayer.ExoPlayer
27+
import androidx.xr.compose.spatial.Subspace
28+
import androidx.xr.compose.subspace.SpatialExternalSurface
29+
import androidx.xr.compose.subspace.StereoMode
30+
import androidx.xr.compose.subspace.layout.SubspaceModifier
31+
import androidx.xr.compose.subspace.layout.height
32+
import androidx.xr.compose.subspace.layout.width
33+
34+
// [START androidxr_compose_SpatialExternalSurfaceStereo]
35+
@Composable
36+
fun SpatialExternalSurfaceContent() {
37+
val context = LocalContext.current
38+
Subspace {
39+
SpatialExternalSurface(
40+
modifier = SubspaceModifier
41+
.width(1200.dp) // Default width is 400.dp if no width modifier is specified
42+
.height(676.dp), // Default height is 400.dp if no height modifier is specified
43+
// Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending
44+
// upon which type of content you are rendering: monoscopic content, side-by-side stereo
45+
// content, or top-bottom stereo content
46+
stereoMode = StereoMode.SideBySide,
47+
) {
48+
val exoPlayer = remember { ExoPlayer.Builder(context).build() }
49+
val videoUri = Uri.Builder()
50+
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
51+
// Represents a side-by-side stereo video, where each frame contains a pair of
52+
// video frames arranged side-by-side. The frame on the left represents the left
53+
// eye view, and the frame on the right represents the right eye view.
54+
.path("sbs_video.mp4")
55+
.build()
56+
val mediaItem = MediaItem.fromUri(videoUri)
57+
58+
// onSurfaceCreated is invoked only one time, when the Surface is created
59+
onSurfaceCreated { surface ->
60+
exoPlayer.setVideoSurface(surface)
61+
exoPlayer.setMediaItem(mediaItem)
62+
exoPlayer.prepare()
63+
exoPlayer.play()
64+
}
65+
// onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its
66+
// associated Surface are destroyed
67+
onSurfaceDestroyed { exoPlayer.release() }
68+
}
69+
}
70+
}
71+
// [END androidxr_compose_SpatialExternalSurfaceStereo]

xr/src/main/java/com/example/xr/compose/SpatialRow.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import androidx.xr.compose.subspace.layout.width
2727
@Composable
2828
private fun SpatialRowExample() {
2929
// [START androidxr_compose_SpatialRowExample]
30-
SpatialRow(curveRadius = 825.dp) {
30+
SpatialRow {
3131
SpatialPanel(
3232
SubspaceModifier
3333
.width(384.dp)

xr/src/main/java/com/example/xr/compose/Subspace.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import androidx.activity.compose.setContent
2222
import androidx.activity.enableEdgeToEdge
2323
import androidx.compose.foundation.layout.Row
2424
import androidx.compose.runtime.Composable
25+
import androidx.xr.compose.spatial.ApplicationSubspace
2526
import androidx.xr.compose.spatial.Subspace
2627
import androidx.xr.compose.subspace.SpatialPanel
2728

@@ -32,7 +33,7 @@ private class SubspaceActivity : ComponentActivity() {
3233
// [START androidxr_compose_SubspaceSetContent]
3334
setContent {
3435
// This is a top-level subspace
35-
Subspace {
36+
ApplicationSubspace {
3637
SpatialPanel {
3738
MyComposable()
3839
}

0 commit comments

Comments
 (0)