diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a2589feb..3b540244c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,6 +62,10 @@ okHttp = "4.12.0" playServicesWearable = "19.0.0" protolayout = "1.2.1" recyclerview = "1.4.0" +# @keep +androidx-xr-arcore = "1.0.0-alpha04" +androidx-xr-scenecore = "1.0.0-alpha04" +androidx-xr-compose = "1.0.0-alpha04" targetSdk = "34" tiles = "1.4.1" version-catalog-update = "1.0.0" @@ -149,9 +153,9 @@ androidx-window = { module = "androidx.window:window", version.ref = "androidx-w androidx-window-core = { module = "androidx.window:window-core", version.ref = "androidx-window-core" } androidx-window-java = { module = "androidx.window:window-java", version.ref = "androidx-window-java" } androidx-work-runtime-ktx = "androidx.work:work-runtime-ktx:2.10.1" -androidx-xr-arcore = { module = "androidx.xr.arcore:arcore", version.ref = "androidx-xr" } -androidx-xr-compose = { module = "androidx.xr.compose:compose", version.ref = "androidx-xr" } -androidx-xr-scenecore = { module = "androidx.xr.scenecore:scenecore", version.ref = "androidx-xr" } +androidx-xr-arcore = { module = "androidx.xr.arcore:arcore", version.ref = "androidx-xr-arcore" } +androidx-xr-compose = { module = "androidx.xr.compose:compose", version.ref = "androidx-xr-compose" } +androidx-xr-scenecore = { module = "androidx.xr.scenecore:scenecore", version.ref = "androidx-xr-scenecore" } appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } coil-kt-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } compose-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearComposeFoundation" } diff --git a/xr/src/main/java/com/example/xr/arcore/Anchors.kt b/xr/src/main/java/com/example/xr/arcore/Anchors.kt index 963da2b43..cb4992095 100644 --- a/xr/src/main/java/com/example/xr/arcore/Anchors.kt +++ b/xr/src/main/java/com/example/xr/arcore/Anchors.kt @@ -19,10 +19,31 @@ package com.example.xr.arcore import androidx.xr.arcore.Anchor import androidx.xr.arcore.AnchorCreateSuccess import androidx.xr.arcore.Trackable +import androidx.xr.runtime.Config import androidx.xr.runtime.Session +import androidx.xr.runtime.SessionConfigureConfigurationNotSupported +import androidx.xr.runtime.SessionConfigurePermissionsNotGranted +import androidx.xr.runtime.SessionConfigureSuccess import androidx.xr.runtime.math.Pose import androidx.xr.scenecore.AnchorEntity import androidx.xr.scenecore.Entity +import androidx.xr.scenecore.scene + +@Suppress("RestrictedApi") // b/416288516 - session.config and session.configure() are incorrectly restricted +fun configureAnchoring(session: Session) { + // [START androidxr_arcore_anchoring_configure] + val newConfig = session.config.copy( + anchorPersistence = Config.AnchorPersistenceMode.Enabled, + ) + when (val result = session.configure(newConfig)) { + is SessionConfigureConfigurationNotSupported -> + TODO(/* Some combinations of configurations are not valid. Handle this failure case. */) + is SessionConfigurePermissionsNotGranted -> + TODO(/* The required permissions in result.permissions have not been granted. */) + is SessionConfigureSuccess -> TODO(/* Success! */) + } + // [END androidxr_arcore_anchoring_configure] +} private fun createAnchorAtPose(session: Session, pose: Pose) { val pose = Pose() @@ -45,13 +66,13 @@ private fun createAnchorAtTrackable(trackable: Trackable<*>) { } private fun attachEntityToAnchor( - session: androidx.xr.scenecore.Session, + session: Session, entity: Entity, anchor: Anchor ) { // [START androidxr_arcore_entity_tracks_anchor] AnchorEntity.create(session, anchor).apply { - setParent(session.activitySpace) + setParent(session.scene.activitySpace) addChild(entity) } // [END androidxr_arcore_entity_tracks_anchor] diff --git a/xr/src/main/java/com/example/xr/arcore/Hands.kt b/xr/src/main/java/com/example/xr/arcore/Hands.kt index 26cc0ba8e..af3a547b0 100644 --- a/xr/src/main/java/com/example/xr/arcore/Hands.kt +++ b/xr/src/main/java/com/example/xr/arcore/Hands.kt @@ -16,59 +16,39 @@ package com.example.xr.arcore -import android.annotation.SuppressLint -import android.os.Bundle import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import androidx.xr.arcore.Hand -import androidx.xr.arcore.HandJointType -import androidx.xr.compose.platform.setSubspaceContent +import androidx.xr.runtime.Config +import androidx.xr.runtime.HandJointType import androidx.xr.runtime.Session +import androidx.xr.runtime.SessionConfigureConfigurationNotSupported +import androidx.xr.runtime.SessionConfigurePermissionsNotGranted +import androidx.xr.runtime.SessionConfigureSuccess import androidx.xr.runtime.math.Pose import androidx.xr.runtime.math.Quaternion import androidx.xr.runtime.math.Vector3 -import androidx.xr.scenecore.Entity -import androidx.xr.scenecore.GltfModel import androidx.xr.scenecore.GltfModelEntity -import kotlinx.coroutines.guava.await +import androidx.xr.scenecore.scene import kotlinx.coroutines.launch -class SampleHandsActivity : ComponentActivity() { - lateinit var session: Session - lateinit var scenecoreSession: androidx.xr.scenecore.Session - lateinit var sessionHelper: SessionLifecycleHelper - - var palmEntity: Entity? = null - var indexFingerEntity: Entity? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setSubspaceContent { } - - scenecoreSession = androidx.xr.scenecore.Session.create(this@SampleHandsActivity) - lifecycleScope.launch { - val model = GltfModel.create(scenecoreSession, "models/saturn_rings.glb").await() - palmEntity = GltfModelEntity.create(scenecoreSession, model).apply { - setScale(0.3f) - setHidden(true) - } - indexFingerEntity = GltfModelEntity.create(scenecoreSession, model).apply { - setScale(0.2f) - setHidden(true) - } - } - - sessionHelper = SessionLifecycleHelper( - onCreateCallback = { session = it }, - onResumeCallback = { - collectHands(session) - } - ) - lifecycle.addObserver(sessionHelper) +@Suppress("RestrictedApi") // b/416288516 - session.config and session.configure() are incorrectly restricted +fun ComponentActivity.configureSession(session: Session) { + // [START androidxr_arcore_hand_configure] + val newConfig = session.config.copy( + handTracking = Config.HandTrackingMode.Enabled + ) + when (val result = session.configure(newConfig)) { + is SessionConfigureConfigurationNotSupported -> + TODO(/* Some combinations of configurations are not valid. Handle this failure case. */) + is SessionConfigurePermissionsNotGranted -> + TODO(/* The required permissions in result.permissions have not been granted. */) + is SessionConfigureSuccess -> TODO(/* Success! */) } + // [END androidxr_arcore_hand_configure] } -fun SampleHandsActivity.collectHands(session: Session) { +fun ComponentActivity.collectHands(session: Session) { lifecycleScope.launch { // [START androidxr_arcore_hand_collect] Hand.left(session)?.state?.collect { handState -> // or Hand.right(session) @@ -85,9 +65,9 @@ fun SampleHandsActivity.collectHands(session: Session) { } } -@SuppressLint("RestrictedApi") // HandJointType is mistakenly @Restrict: b/397415504 -fun SampleHandsActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) { - val palmEntity = palmEntity ?: return +fun ComponentActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) { + val session: Session = null!! + val palmEntity: GltfModelEntity = null!! // [START androidxr_arcore_hand_entityAtHandPalm] val palmPose = leftHandState.handJoints[HandJointType.PALM] ?: return @@ -96,18 +76,18 @@ fun SampleHandsActivity.renderPlanetAtHandPalm(leftHandState: Hand.State) { palmEntity.setHidden(angle > Math.toRadians(40.0)) val transformedPose = - scenecoreSession.perceptionSpace.transformPoseTo( + session.scene.perceptionSpace.transformPoseTo( palmPose, - scenecoreSession.activitySpace, + session.scene.activitySpace, ) val newPosition = transformedPose.translation + transformedPose.down * 0.05f palmEntity.setPose(Pose(newPosition, transformedPose.rotation)) // [END androidxr_arcore_hand_entityAtHandPalm] } -@SuppressLint("RestrictedApi") // HandJointType is mistakenly @Restrict: b/397415504 -fun SampleHandsActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) { - val indexFingerEntity = indexFingerEntity ?: return +fun ComponentActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) { + val session: Session = null!! + val indexFingerEntity: GltfModelEntity = null!! // [START androidxr_arcore_hand_entityAtIndexFingerTip] val tipPose = rightHandState.handJoints[HandJointType.INDEX_TIP] ?: return @@ -117,9 +97,9 @@ fun SampleHandsActivity.renderPlanetAtFingerTip(rightHandState: Hand.State) { indexFingerEntity.setHidden(angle > Math.toRadians(40.0)) val transformedPose = - scenecoreSession.perceptionSpace.transformPoseTo( + session.scene.perceptionSpace.transformPoseTo( tipPose, - scenecoreSession.activitySpace, + session.scene.activitySpace, ) val position = transformedPose.translation + transformedPose.forward * 0.03f val rotation = Quaternion.fromLookTowards(transformedPose.up, Vector3.Up) diff --git a/xr/src/main/java/com/example/xr/arcore/Planes.kt b/xr/src/main/java/com/example/xr/arcore/Planes.kt index 975523ae8..fd5e02c11 100644 --- a/xr/src/main/java/com/example/xr/arcore/Planes.kt +++ b/xr/src/main/java/com/example/xr/arcore/Planes.kt @@ -17,9 +17,30 @@ package com.example.xr.arcore import androidx.xr.arcore.Plane +import androidx.xr.runtime.Config import androidx.xr.runtime.Session +import androidx.xr.runtime.SessionConfigureConfigurationNotSupported +import androidx.xr.runtime.SessionConfigurePermissionsNotGranted +import androidx.xr.runtime.SessionConfigureSuccess import androidx.xr.runtime.math.Pose import androidx.xr.runtime.math.Ray +import androidx.xr.scenecore.scene + +@Suppress("RestrictedApi") // b/416288516 - session.config and session.configure() are incorrectly restricted +fun configurePlaneTracking(session: Session) { + // [START androidxr_arcore_planetracking_configure] + val newConfig = session.config.copy( + planeTracking = Config.PlaneTrackingMode.HorizontalAndVertical, + ) + when (val result = session.configure(newConfig)) { + is SessionConfigureConfigurationNotSupported -> + TODO(/* Some combinations of configurations are not valid. Handle this failure case. */) + is SessionConfigurePermissionsNotGranted -> + TODO(/* The required permissions in result.permissions have not been granted. */) + is SessionConfigureSuccess -> TODO(/* Success! */) + } + // [END androidxr_arcore_planetracking_configure] +} private suspend fun subscribePlanes(session: Session) { // [START androidxr_arcore_planes_subscribe] @@ -30,8 +51,7 @@ private suspend fun subscribePlanes(session: Session) { } private fun hitTestTable(session: Session) { - val scenecoreSession: androidx.xr.scenecore.Session = null!! - val pose = scenecoreSession.spatialUser.head?.transformPoseTo(Pose(), scenecoreSession.perceptionSpace) ?: return + val pose = session.scene.spatialUser.head?.transformPoseTo(Pose(), session.scene.perceptionSpace) ?: return val ray = Ray(pose.translation, pose.forward) // [START androidxr_arcore_hitTest] val results = androidx.xr.arcore.hitTest(session, ray) diff --git a/xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt b/xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt deleted file mode 100644 index 77462257f..000000000 --- a/xr/src/main/java/com/example/xr/arcore/SessionLifecycleHelper.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.xr.arcore - -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.xr.runtime.Session - -/** - * This is a dummy version of [SessionLifecycleHelper](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:xr/arcore/integration-tests/whitebox/src/main/kotlin/androidx/xr/arcore/apps/whitebox/common/SessionLifecycleHelper.kt). - * This will be removed when Session becomes a LifecycleOwner in cl/726643897. - */ -class SessionLifecycleHelper( - val onCreateCallback: (Session) -> Unit, - - val onResumeCallback: (() -> Unit)? = null, -) : DefaultLifecycleObserver diff --git a/xr/src/main/java/com/example/xr/compose/SpatialExternalSurface.kt b/xr/src/main/java/com/example/xr/compose/SpatialExternalSurface.kt new file mode 100644 index 000000000..1736bc909 --- /dev/null +++ b/xr/src/main/java/com/example/xr/compose/SpatialExternalSurface.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.xr.compose + +import android.content.ContentResolver +import android.net.Uri +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.unit.dp +import androidx.media3.common.MediaItem +import androidx.media3.exoplayer.ExoPlayer +import androidx.xr.compose.spatial.Subspace +import androidx.xr.compose.subspace.SpatialExternalSurface +import androidx.xr.compose.subspace.StereoMode +import androidx.xr.compose.subspace.layout.SubspaceModifier +import androidx.xr.compose.subspace.layout.height +import androidx.xr.compose.subspace.layout.width + +// [START androidxr_compose_SpatialExternalSurfaceStereo] +@Composable +fun SpatialExternalSurfaceContent() { + val context = LocalContext.current + Subspace { + SpatialExternalSurface( + modifier = SubspaceModifier + .width(1200.dp) // Default width is 400.dp if no width modifier is specified + .height(676.dp), // Default height is 400.dp if no height modifier is specified + // Use StereoMode.Mono, StereoMode.SideBySide, or StereoMode.TopBottom, depending + // upon which type of content you are rendering: monoscopic content, side-by-side stereo + // content, or top-bottom stereo content + stereoMode = StereoMode.SideBySide, + ) { + val exoPlayer = remember { ExoPlayer.Builder(context).build() } + val videoUri = Uri.Builder() + .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) + // Represents a side-by-side stereo video, where each frame contains a pair of + // video frames arranged side-by-side. The frame on the left represents the left + // eye view, and the frame on the right represents the right eye view. + .path("sbs_video.mp4") + .build() + val mediaItem = MediaItem.fromUri(videoUri) + + // onSurfaceCreated is invoked only one time, when the Surface is created + onSurfaceCreated { surface -> + exoPlayer.setVideoSurface(surface) + exoPlayer.setMediaItem(mediaItem) + exoPlayer.prepare() + exoPlayer.play() + } + // onSurfaceDestroyed is invoked when the SpatialExternalSurface composable and its + // associated Surface are destroyed + onSurfaceDestroyed { exoPlayer.release() } + } + } +} +// [END androidxr_compose_SpatialExternalSurfaceStereo] diff --git a/xr/src/main/java/com/example/xr/compose/SpatialRow.kt b/xr/src/main/java/com/example/xr/compose/SpatialRow.kt index 2c0ccb955..d138411ac 100644 --- a/xr/src/main/java/com/example/xr/compose/SpatialRow.kt +++ b/xr/src/main/java/com/example/xr/compose/SpatialRow.kt @@ -27,7 +27,7 @@ import androidx.xr.compose.subspace.layout.width @Composable private fun SpatialRowExample() { // [START androidxr_compose_SpatialRowExample] - SpatialRow(curveRadius = 825.dp) { + SpatialRow { SpatialPanel( SubspaceModifier .width(384.dp) diff --git a/xr/src/main/java/com/example/xr/compose/Subspace.kt b/xr/src/main/java/com/example/xr/compose/Subspace.kt index 75579968e..2cbbe1021 100644 --- a/xr/src/main/java/com/example/xr/compose/Subspace.kt +++ b/xr/src/main/java/com/example/xr/compose/Subspace.kt @@ -22,6 +22,7 @@ import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.layout.Row import androidx.compose.runtime.Composable +import androidx.xr.compose.spatial.ApplicationSubspace import androidx.xr.compose.spatial.Subspace import androidx.xr.compose.subspace.SpatialPanel @@ -32,7 +33,7 @@ private class SubspaceActivity : ComponentActivity() { // [START androidxr_compose_SubspaceSetContent] setContent { // This is a top-level subspace - Subspace { + ApplicationSubspace { SpatialPanel { MyComposable() } diff --git a/xr/src/main/java/com/example/xr/compose/Views.kt b/xr/src/main/java/com/example/xr/compose/Views.kt index 20a8b4d74..3f91ef2ad 100644 --- a/xr/src/main/java/com/example/xr/compose/Views.kt +++ b/xr/src/main/java/com/example/xr/compose/Views.kt @@ -33,9 +33,9 @@ import androidx.xr.compose.subspace.layout.SubspaceModifier import androidx.xr.compose.subspace.layout.depth import androidx.xr.compose.subspace.layout.height import androidx.xr.compose.subspace.layout.width -import androidx.xr.scenecore.Dimensions +import androidx.xr.runtime.Session import androidx.xr.scenecore.PanelEntity -import androidx.xr.scenecore.Session +import androidx.xr.scenecore.PixelDimensions import com.example.xr.R private class MyCustomView(context: Context) : View(context) @@ -46,9 +46,8 @@ private class ActivityWithSubspaceContent : ComponentActivity() { // [START androidxr_compose_ActivityWithSubspaceContent] setSubspaceContent { SpatialPanel( - view = MyCustomView(this), modifier = SubspaceModifier.height(500.dp).width(500.dp).depth(25.dp) - ) + ) { MyCustomView(this) } } // [END androidxr_compose_ActivityWithSubspaceContent] } @@ -84,8 +83,7 @@ fun ComponentActivity.PanelEntityWithView(xrSession: Session) { val panelEntity = PanelEntity.create( session = xrSession, view = panelContent, - surfaceDimensionsPx = Dimensions(500f, 500f), - dimensions = Dimensions(1f, 1f, 1f), + pixelDimensions = PixelDimensions(500, 500), name = "panel entity" ) // [END androidxr_compose_PanelEntityWithView] diff --git a/xr/src/main/java/com/example/xr/misc/ModeTransition.kt b/xr/src/main/java/com/example/xr/misc/ModeTransition.kt index c34d549ae..dca0ddbfb 100644 --- a/xr/src/main/java/com/example/xr/misc/ModeTransition.kt +++ b/xr/src/main/java/com/example/xr/misc/ModeTransition.kt @@ -18,7 +18,8 @@ package com.example.xr.misc import androidx.compose.runtime.Composable import androidx.xr.compose.platform.LocalSpatialConfiguration -import androidx.xr.scenecore.Session +import androidx.xr.runtime.Session +import androidx.xr.scenecore.scene @Composable fun modeTransitionCompose() { @@ -31,6 +32,6 @@ fun modeTransitionCompose() { fun modeTransitionScenecore(xrSession: Session) { // [START androidxr_misc_modeTransitionScenecore] - xrSession.spatialEnvironment.requestHomeSpaceMode() + xrSession.scene.spatialEnvironment.requestHomeSpaceMode() // [END androidxr_misc_modeTransitionScenecore] } diff --git a/xr/src/main/java/com/example/xr/runtime/Session.kt b/xr/src/main/java/com/example/xr/runtime/Session.kt new file mode 100644 index 000000000..f2fd85a2a --- /dev/null +++ b/xr/src/main/java/com/example/xr/runtime/Session.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.xr.runtime + +import android.app.Activity +import androidx.compose.runtime.Composable +import androidx.xr.compose.platform.LocalSession +import androidx.xr.runtime.Session +import androidx.xr.runtime.SessionCreatePermissionsNotGranted +import androidx.xr.runtime.SessionCreateSuccess +import androidx.xr.runtime.SessionResumePermissionsNotGranted +import androidx.xr.runtime.SessionResumeSuccess + +// [START androidxr_localsession] +@Composable +fun ComposableUsingSession() { + val session = LocalSession.current +} +// [END androidxr_localsession] + +fun Activity.createSession() { + // [START androidxr_session_create] + when (val result = Session.create(this)) { + is SessionCreateSuccess -> { + val xrSession = result.session + // ... + } + is SessionCreatePermissionsNotGranted -> + TODO(/* The required permissions in result.permissions have not been granted. */) + } + // [END androidxr_session_create] +} + +fun sessionResume(session: Session) { + // [START androidxr_session_resume] + when (val result = session.resume()) { + is SessionResumeSuccess -> { + // Session has been created successfully. + // Attach any successful handlers here. + } + + is SessionResumePermissionsNotGranted -> { + // Request permissions in `result.permissions`. + } + } + // [END androidxr_session_resume] +} diff --git a/xr/src/main/java/com/example/xr/scenecore/Entities.kt b/xr/src/main/java/com/example/xr/scenecore/Entities.kt index f2bb48f6e..7c8046664 100644 --- a/xr/src/main/java/com/example/xr/scenecore/Entities.kt +++ b/xr/src/main/java/com/example/xr/scenecore/Entities.kt @@ -16,6 +16,7 @@ package com.example.xr.scenecore +import androidx.xr.runtime.Session import androidx.xr.runtime.math.Pose import androidx.xr.runtime.math.Quaternion import androidx.xr.runtime.math.Vector3 @@ -28,7 +29,6 @@ import androidx.xr.scenecore.MovableComponent import androidx.xr.scenecore.PlaneSemantic import androidx.xr.scenecore.PlaneType import androidx.xr.scenecore.ResizableComponent -import androidx.xr.scenecore.Session import java.util.concurrent.Executors private fun setPoseExample(entity: Entity) { diff --git a/xr/src/main/java/com/example/xr/scenecore/Environments.kt b/xr/src/main/java/com/example/xr/scenecore/Environments.kt index a366f9d12..35f753569 100644 --- a/xr/src/main/java/com/example/xr/scenecore/Environments.kt +++ b/xr/src/main/java/com/example/xr/scenecore/Environments.kt @@ -16,10 +16,11 @@ package com.example.xr.scenecore +import androidx.xr.runtime.Session import androidx.xr.scenecore.ExrImage import androidx.xr.scenecore.GltfModel -import androidx.xr.scenecore.Session import androidx.xr.scenecore.SpatialEnvironment +import androidx.xr.scenecore.scene import kotlinx.coroutines.guava.await private class Environments(val session: Session) { @@ -32,16 +33,16 @@ private class Environments(val session: Session) { fun loadEnvironmentSkybox() { // [START androidxr_scenecore_environment_loadEnvironmentSkybox] - val skybox = ExrImage.create(session, "BlueSkybox.exr") + val lightingForSkybox = ExrImage.create(session, "BlueSkyboxLighting.zip") // [END androidxr_scenecore_environment_loadEnvironmentSkybox] } - fun setEnvironmentPreference(environmentGeometry: GltfModel, skybox: ExrImage) { + fun setEnvironmentPreference(environmentGeometry: GltfModel, lightingForSkybox: ExrImage) { // [START androidxr_scenecore_environment_setEnvironmentPreference] val spatialEnvironmentPreference = - SpatialEnvironment.SpatialEnvironmentPreference(skybox, environmentGeometry) + SpatialEnvironment.SpatialEnvironmentPreference(lightingForSkybox, environmentGeometry) val preferenceResult = - session.spatialEnvironment.setSpatialEnvironmentPreference(spatialEnvironmentPreference) + session.scene.spatialEnvironment.setSpatialEnvironmentPreference(spatialEnvironmentPreference) if (preferenceResult == SpatialEnvironment.SetSpatialEnvironmentPreferenceChangeApplied()) { // The environment was successfully updated and is now visible, and any listeners // specified using addOnSpatialEnvironmentChangedListener will be notified. @@ -54,7 +55,7 @@ private class Environments(val session: Session) { fun setPassthroughOpacityPreference() { // [START androidxr_scenecore_environment_setPassthroughOpacityPreference] - val preferenceResult = session.spatialEnvironment.setPassthroughOpacityPreference(1.0f) + val preferenceResult = session.scene.spatialEnvironment.setPassthroughOpacityPreference(1.0f) if (preferenceResult == SpatialEnvironment.SetPassthroughOpacityPreferenceChangeApplied()) { // The passthrough opacity request succeeded and should be visible now, and any listeners // specified using addOnPassthroughOpacityChangedListener will be notified @@ -71,7 +72,7 @@ private class Environments(val session: Session) { fun getCurrentPassthroughOpacity() { // [START androidxr_scenecore_environment_getCurrentPassthroughOpacity] - val currentPassthroughOpacity = session.spatialEnvironment.getCurrentPassthroughOpacity() + val currentPassthroughOpacity = session.scene.spatialEnvironment.getCurrentPassthroughOpacity() // [END androidxr_scenecore_environment_getCurrentPassthroughOpacity] } } diff --git a/xr/src/main/java/com/example/xr/scenecore/GltfEntity.kt b/xr/src/main/java/com/example/xr/scenecore/GltfEntity.kt index 6be959836..586f53fb3 100644 --- a/xr/src/main/java/com/example/xr/scenecore/GltfEntity.kt +++ b/xr/src/main/java/com/example/xr/scenecore/GltfEntity.kt @@ -19,11 +19,11 @@ package com.example.xr.scenecore import android.content.Intent import android.net.Uri import androidx.activity.ComponentActivity +import androidx.xr.runtime.Session import androidx.xr.scenecore.GltfModel import androidx.xr.scenecore.GltfModelEntity -import androidx.xr.scenecore.Session import androidx.xr.scenecore.SpatialCapabilities -import androidx.xr.scenecore.getSpatialCapabilities +import androidx.xr.scenecore.scene import kotlinx.coroutines.guava.await private suspend fun loadGltfFile(session: Session) { @@ -34,7 +34,7 @@ private suspend fun loadGltfFile(session: Session) { private fun createModelEntity(session: Session, gltfModel: GltfModel) { // [START androidxr_scenecore_gltfmodelentity_create] - if (session.getSpatialCapabilities() + if (session.scene.spatialCapabilities .hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT) ) { val gltfEntity = GltfModelEntity.create(session, gltfModel) diff --git a/xr/src/main/java/com/example/xr/scenecore/SpatialAudio.kt b/xr/src/main/java/com/example/xr/scenecore/SpatialAudio.kt index 8bc9e99ee..1d1eac1ae 100644 --- a/xr/src/main/java/com/example/xr/scenecore/SpatialAudio.kt +++ b/xr/src/main/java/com/example/xr/scenecore/SpatialAudio.kt @@ -22,20 +22,20 @@ import android.media.AudioAttributes.CONTENT_TYPE_SONIFICATION import android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION import android.media.MediaPlayer import android.media.SoundPool +import androidx.xr.runtime.Session import androidx.xr.scenecore.Entity -import androidx.xr.scenecore.PointSourceAttributes -import androidx.xr.scenecore.Session +import androidx.xr.scenecore.PointSourceParams import androidx.xr.scenecore.SoundFieldAttributes import androidx.xr.scenecore.SpatialCapabilities import androidx.xr.scenecore.SpatialMediaPlayer import androidx.xr.scenecore.SpatialSoundPool import androidx.xr.scenecore.SpatializerConstants -import androidx.xr.scenecore.getSpatialCapabilities +import androidx.xr.scenecore.scene private fun playSpatialAudioAtEntity(session: Session, appContext: Context, entity: Entity) { // [START androidxr_scenecore_playSpatialAudio] // Check spatial capabilities before using spatial audio - if (session.getSpatialCapabilities() + if (session.scene.spatialCapabilities .hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO) ) { // The session has spatial audio capabilities val maxVolume = 1F @@ -52,7 +52,7 @@ private fun playSpatialAudioAtEntity(session: Session, appContext: Context, enti ) .build() - val pointSource = PointSourceAttributes(entity) + val pointSource = PointSourceParams(entity) val soundEffect = appContext.assets.openFd("sounds/tiger_16db.mp3") val pointSoundId = soundPool.load(soundEffect, lowPriority) @@ -64,7 +64,7 @@ private fun playSpatialAudioAtEntity(session: Session, appContext: Context, enti session = session, soundPool = soundPool, soundID = pointSoundId, - attributes = pointSource, + params = pointSource, volume = maxVolume, priority = lowPriority, loop = infiniteLoop, @@ -81,10 +81,10 @@ private fun playSpatialAudioAtEntity(session: Session, appContext: Context, enti private fun playSpatialAudioAtEntitySurround(session: Session, appContext: Context) { // [START androidxr_scenecore_playSpatialAudioSurround] // Check spatial capabilities before using spatial audio - if (session.getSpatialCapabilities().hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) { + if (session.scene.spatialCapabilities.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) { // The session has spatial audio capabilities - val pointSourceAttributes = PointSourceAttributes(session.mainPanelEntity) + val pointSourceAttributes = PointSourceParams(session.scene.mainPanelEntity) val mediaPlayer = MediaPlayer() @@ -98,7 +98,7 @@ private fun playSpatialAudioAtEntitySurround(session: Session, appContext: Conte .setUsage(AudioAttributes.USAGE_MEDIA) .build() - SpatialMediaPlayer.setPointSourceAttributes( + SpatialMediaPlayer.setPointSourceParams( session, mediaPlayer, pointSourceAttributes @@ -116,7 +116,7 @@ private fun playSpatialAudioAtEntitySurround(session: Session, appContext: Conte private fun playSpatialAudioAtEntityAmbionics(session: Session, appContext: Context) { // [START androidxr_scenecore_playSpatialAudioAmbionics] // Check spatial capabilities before using spatial audio - if (session.getSpatialCapabilities().hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) { + if (session.scene.spatialCapabilities.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)) { // The session has spatial audio capabilities val soundFieldAttributes = diff --git a/xr/src/main/java/com/example/xr/scenecore/SpatialCapabilities.kt b/xr/src/main/java/com/example/xr/scenecore/SpatialCapabilities.kt index 9d041eeac..fcfcdf5a8 100644 --- a/xr/src/main/java/com/example/xr/scenecore/SpatialCapabilities.kt +++ b/xr/src/main/java/com/example/xr/scenecore/SpatialCapabilities.kt @@ -16,21 +16,21 @@ package com.example.xr.scenecore -import androidx.xr.scenecore.Session +import androidx.xr.runtime.Session import androidx.xr.scenecore.SpatialCapabilities -import androidx.xr.scenecore.getSpatialCapabilities +import androidx.xr.scenecore.scene fun checkMultipleCapabilities(xrSession: Session) { // [START androidxr_compose_checkMultipleCapabilities] // Example 1: check if enabling passthrough mode is allowed - if (xrSession.getSpatialCapabilities().hasCapability( + if (xrSession.scene.spatialCapabilities.hasCapability( SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL ) ) { - xrSession.spatialEnvironment.setPassthroughOpacityPreference(0f) + xrSession.scene.spatialEnvironment.setPassthroughOpacityPreference(0f) } // Example 2: multiple capability flags can be checked simultaneously: - if (xrSession.getSpatialCapabilities().hasCapability( + if (xrSession.scene.spatialCapabilities.hasCapability( SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL and SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT ) diff --git a/xr/src/main/java/com/example/xr/scenecore/SpatialVideo.kt b/xr/src/main/java/com/example/xr/scenecore/SpatialVideo.kt index df4a20d23..272c21a8a 100644 --- a/xr/src/main/java/com/example/xr/scenecore/SpatialVideo.kt +++ b/xr/src/main/java/com/example/xr/scenecore/SpatialVideo.kt @@ -21,19 +21,20 @@ import android.net.Uri import androidx.activity.ComponentActivity import androidx.media3.common.MediaItem import androidx.media3.exoplayer.ExoPlayer +import androidx.xr.runtime.Session import androidx.xr.runtime.math.Pose import androidx.xr.runtime.math.Vector3 -import androidx.xr.scenecore.Session -import androidx.xr.scenecore.StereoSurfaceEntity +import androidx.xr.scenecore.SurfaceEntity +import androidx.xr.scenecore.scene private fun ComponentActivity.surfaceEntityCreate(xrSession: Session) { // [START androidxr_scenecore_surfaceEntityCreate] - val stereoSurfaceEntity = StereoSurfaceEntity.create( + val stereoSurfaceEntity = SurfaceEntity.create( xrSession, - StereoSurfaceEntity.StereoMode.SIDE_BY_SIDE, + SurfaceEntity.StereoMode.SIDE_BY_SIDE, // Position 1.5 meters in front of user Pose(Vector3(0.0f, 0.0f, -1.5f)), - StereoSurfaceEntity.CanvasShape.Quad(1.0f, 1.0f) + SurfaceEntity.CanvasShape.Quad(1.0f, 1.0f) ) val videoUri = Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) @@ -53,14 +54,14 @@ private fun ComponentActivity.surfaceEntityCreateSbs(xrSession: Session) { // [START androidxr_scenecore_surfaceEntityCreateSbs] // Set up the surface for playing a 180° video on a hemisphere. val hemisphereStereoSurfaceEntity = - StereoSurfaceEntity.create( + SurfaceEntity.create( xrSession, - StereoSurfaceEntity.StereoMode.SIDE_BY_SIDE, - xrSession.spatialUser.head?.transformPoseTo( + SurfaceEntity.StereoMode.SIDE_BY_SIDE, + xrSession.scene.spatialUser.head?.transformPoseTo( Pose.Identity, - xrSession.activitySpace + xrSession.scene.activitySpace )!!, - StereoSurfaceEntity.CanvasShape.Vr180Hemisphere(1.0f), + SurfaceEntity.CanvasShape.Vr180Hemisphere(1.0f), ) // ... and use the surface for playing the media. // [END androidxr_scenecore_surfaceEntityCreateSbs] @@ -70,14 +71,14 @@ private fun ComponentActivity.surfaceEntityCreateTb(xrSession: Session) { // [START androidxr_scenecore_surfaceEntityCreateTb] // Set up the surface for playing a 360° video on a sphere. val sphereStereoSurfaceEntity = - StereoSurfaceEntity.create( + SurfaceEntity.create( xrSession, - StereoSurfaceEntity.StereoMode.TOP_BOTTOM, - xrSession.spatialUser.head?.transformPoseTo( + SurfaceEntity.StereoMode.TOP_BOTTOM, + xrSession.scene.spatialUser.head?.transformPoseTo( Pose.Identity, - xrSession.activitySpace + xrSession.scene.activitySpace )!!, - StereoSurfaceEntity.CanvasShape.Vr360Sphere(1.0f), + SurfaceEntity.CanvasShape.Vr360Sphere(1.0f), ) // ... and use the surface for playing the media. // [END androidxr_scenecore_surfaceEntityCreateTb]