
onSceneReady, onVRReady, and onSpatialShutdown that help you manage the state of your XR app throughout its lifecycle.Activity (Android) ↓ VrActivity (Spatial SDK) ↓ AppSystemActivity (Spatial SDK) ↓ YourActivity (Your immersive app)
AppSystemActivity for the immersive activity. However, for your panel activities (either hybrid activities or the activity-based panels), you can simply extend Android’s Activity class.onCreate is called when the activity is first created. In a Spatial SDK app, this is where:override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // Important: Always call super.onCreate() first
// Register components
componentManager.registerComponent<MyCustomComponent>(MyCustomComponent.Companion)
// Register systems
systemManager.registerSystem(MyCustomSystem())
// Load resources
loadGLXF { composition ->
// Handle loaded 3D models
val environmentEntity = composition.getNodeByName("environment").entity
// Configure entity components
}
}
onStartVrActivity implementation forwards this call to all registered features via featureManager.onStart().onResumeonPauseonSpatialShutdown().onStoponSpatialShutdown() instead as onStop is not guaranteed to be called.onDestroyonSpatialShutdown() instead as onDestroy is not guaranteed to be called.android:configChanges to your immersive activity declaration in AndroidManifest.xml:<activity
android:name=".ImmersiveActivity"
android:configChanges="screenSize|screenLayout|orientation|keyboardHidden|keyboard|navigation|uiMode"
android:launchMode="singleTask"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.oculus.intent.category.VR" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
onConfigurationChanged() instead of destroying and recreating your activity. The Spatial SDK handles common configuration changes internally, so you typically don’t need to override onConfigurationChanged() in your activity.| Value | Purpose |
|---|---|
uiMode | Handles VR mode transitions and display configuration changes |
screenSize | Handles virtual screen dimension adjustments |
screenLayout | Handles layout direction and size class changes |
orientation | Prevents restarts when orientation events occur (VR apps use landscape) |
keyboardHidden | Handles virtual keyboard visibility changes |
keyboard | Handles keyboard availability changes |
navigation | Handles navigation method changes |
configChangesandroid:configChanges attribute, your app may exhibit these symptoms on Horizon OS v85 and later:onSpatialShutdown)android:configChanges attribute with the values listed above.AppSystemActivity): Use the full set of configChanges values shown aboveActivity or ComponentActivity): May use a subset such as screenSize|smallestScreenSize|screenLayout|orientation, or omit the attribute entirely if activity recreation is acceptableonVrReady is triggered when 3D graphics is ready and loaded, but it’s not tied to any Android lifecycle. This section describes each callback in detail.onSceneReadyonResume(), after the scene is loaded in onCreate(). This is the ideal place to:override fun onSceneReady() {
super.onSceneReady() // Important: Always call super.onSceneReady() first
// Set the reference space for tracking
scene.setReferenceSpace(ReferenceSpace.LOCAL_FLOOR)
// Configure lighting
scene.setLightingEnvironment(
ambientColor = Vector3(0f),
sunColor = Vector3(7.0f, 7.0f, 7.0f),
sunDirection = -Vector3(1.0f, 3.0f, -2.0f),
environmentIntensity = 0.3f
)
// Set environment map
scene.updateIBLEnvironment("environment.env")
// Set the view origin
scene.setViewOrigin(0.0f, 0.0f, 0.0f, 0.0f)
// Create entities in the scene
Entity.create(
listOf(
Mesh(Uri.parse("mesh://skybox"), hittable = MeshCollision.NoCollision),
Material().apply {
baseTextureAndroidResourceId = R.drawable.skydome
unlit = true
},
Transform(Pose(Vector3(x = 0f, y = 0f, z = 0f)))
)
)
}
onSceneTickonVRReadyonSceneReady as it specifically indicates that the VR subsystems (like tracking, controllers, and so on) are ready for use.onVRPauseonHMDMountedonHMDUnmountedonRecenterisUserInitiated flag like so override fun onRecenter(isUserInitiated: Boolean).onSpatialShutdownonStop or onDestroy is not guaranteed to be called, onSpatialShutdown is guaranteed to be called before the activity is destroyed. This is useful for doing any final cleanup of Spatial SDK-related resources, such as entities and scene objects.override fun onSpatialShutdown() {
// Destroy entities
entity.destroy()
super.onSpatialShutdown() // Important: Always call super.onSpatialShutdown() last
}
registerFeaturesonCreate to register the features that your app will use. Features are the building blocks of Spatial SDK functionality, such as VR support, input handling, animations, and debugging tools.override fun registerFeatures(): List<SpatialFeature> {
val features = mutableListOf<SpatialFeature>(
VRFeature(this),
PanelAnimationFeature()
)
// Add debug features in development builds
if (BuildConfig.DEBUG) {
features.add(CastInputForwardFeature(this))
features.add(HotReloadFeature(this))
features.add(DataModelInspectorFeature(spatial, this.componentManager))
}
return features
}
registerPanelsonCreate to register the UI panels that your app will use. Panels are 2D UI surfaces that can be placed in 3D space.override fun registerPanels(): List<PanelRegistration> {
return listOf(
LayoutXMLPanelRegistration(
R.id.main_panel,
layoutIdCreator = { R.layout.main_panel },
settingsCreator = {
UIPanelSettings(
shape = CylinderShapeOptions(radius = 1.0f),
display = DpDisplayOptions(width = 720f),
style = PanelStyleOptions(themeResourceId = R.style.PanelAppThemeTransparent),
rendering = LayerRenderOptions()
)
}
),
LayoutXMLPanelRegistration(
R.id.settings_panel,
layoutIdCreator = { R.layout.settings_panel },
settingsCreator = { UIPanelSettings() }
)
)
}