@reactvision/react-viro 2.54.0 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/README.md +85 -46
  2. package/android/react_viro/react_viro-release.aar +0 -0
  3. package/android/viro_renderer/viro_renderer-release.aar +0 -0
  4. package/components/AR/ViroARCamera.tsx +5 -0
  5. package/components/AR/ViroARImageMarker.tsx +5 -0
  6. package/components/AR/ViroARObjectMarker.tsx +5 -0
  7. package/components/AR/ViroARPlane.tsx +5 -0
  8. package/components/AR/ViroARPlaneSelector.tsx +5 -0
  9. package/components/AR/ViroARScene.tsx +5 -0
  10. package/components/AR/ViroARSceneNavigator.tsx +54 -0
  11. package/components/Studio/StudioARScene.tsx +368 -0
  12. package/components/Studio/StudioSceneNavigator.tsx +191 -0
  13. package/components/Studio/VRTStudioModule.ts +40 -0
  14. package/components/Studio/domain/animationRegistry.ts +86 -0
  15. package/components/Studio/domain/collisionBindingsRuntime.ts +93 -0
  16. package/components/Studio/domain/collisionPairKey.ts +15 -0
  17. package/components/Studio/domain/dragConfiguration.ts +48 -0
  18. package/components/Studio/domain/materialConfig.ts +276 -0
  19. package/components/Studio/domain/physicsConfig.ts +204 -0
  20. package/components/Studio/domain/sceneNavigationHandler.ts +150 -0
  21. package/components/Studio/domain/studioMaterials.ts +33 -0
  22. package/components/Studio/domain/triggerImageRegistry.ts +64 -0
  23. package/components/Studio/domain/useStudioShaderTimeUniforms.ts +51 -0
  24. package/components/Studio/domain/useStudioShaderViewportUniforms.ts +52 -0
  25. package/components/Studio/domain/viroNodeFactory.tsx +323 -0
  26. package/components/Studio/index.ts +18 -0
  27. package/components/Studio/types.ts +164 -0
  28. package/components/Types/ViroEvents.ts +53 -0
  29. package/components/Utilities/VRModuleOpenXR.ts +50 -0
  30. package/components/Utilities/VRQuestNavigatorBridge.ts +168 -0
  31. package/components/Utilities/ViroPlatform.ts +52 -0
  32. package/components/Utilities/ViroVersion.ts +1 -1
  33. package/components/Utilities/useAnySourceHover.ts +55 -0
  34. package/components/Utilities/useAnySourcePressed.ts +70 -0
  35. package/components/ViroQuestEntryPoint.tsx +79 -0
  36. package/components/ViroVRSceneNavigator.tsx +44 -19
  37. package/components/ViroXRSceneNavigator.tsx +217 -0
  38. package/components/VisionOS/ViroVisionOSModule.ts +93 -0
  39. package/dist/components/AR/ViroARCamera.d.ts +1 -1
  40. package/dist/components/AR/ViroARCamera.js +5 -0
  41. package/dist/components/AR/ViroARImageMarker.d.ts +1 -1
  42. package/dist/components/AR/ViroARImageMarker.js +5 -0
  43. package/dist/components/AR/ViroARObjectMarker.d.ts +1 -1
  44. package/dist/components/AR/ViroARObjectMarker.js +5 -0
  45. package/dist/components/AR/ViroARPlane.d.ts +1 -1
  46. package/dist/components/AR/ViroARPlane.js +5 -0
  47. package/dist/components/AR/ViroARPlaneSelector.d.ts +1 -1
  48. package/dist/components/AR/ViroARPlaneSelector.js +5 -0
  49. package/dist/components/AR/ViroARScene.d.ts +1 -1
  50. package/dist/components/AR/ViroARScene.js +5 -0
  51. package/dist/components/AR/ViroARSceneNavigator.d.ts +13 -0
  52. package/dist/components/AR/ViroARSceneNavigator.js +36 -0
  53. package/dist/components/Studio/StudioARScene.d.ts +15 -0
  54. package/dist/components/Studio/StudioARScene.js +299 -0
  55. package/dist/components/Studio/StudioSceneNavigator.d.ts +31 -0
  56. package/dist/components/Studio/StudioSceneNavigator.js +174 -0
  57. package/dist/components/Studio/VRTStudioModule.d.ts +15 -0
  58. package/dist/components/Studio/VRTStudioModule.js +31 -0
  59. package/dist/components/Studio/domain/animationRegistry.d.ts +11 -0
  60. package/dist/components/Studio/domain/animationRegistry.js +67 -0
  61. package/dist/components/Studio/domain/collisionBindingsRuntime.d.ts +21 -0
  62. package/dist/components/Studio/domain/collisionBindingsRuntime.js +54 -0
  63. package/dist/components/Studio/domain/collisionPairKey.d.ts +8 -0
  64. package/dist/components/Studio/domain/collisionPairKey.js +15 -0
  65. package/dist/components/Studio/domain/dragConfiguration.d.ts +20 -0
  66. package/dist/components/Studio/domain/dragConfiguration.js +37 -0
  67. package/dist/components/Studio/domain/materialConfig.d.ts +56 -0
  68. package/dist/components/Studio/domain/materialConfig.js +239 -0
  69. package/dist/components/Studio/domain/physicsConfig.d.ts +69 -0
  70. package/dist/components/Studio/domain/physicsConfig.js +165 -0
  71. package/dist/components/Studio/domain/sceneNavigationHandler.d.ts +12 -0
  72. package/dist/components/Studio/domain/sceneNavigationHandler.js +112 -0
  73. package/dist/components/Studio/domain/studioMaterials.d.ts +6 -0
  74. package/dist/components/Studio/domain/studioMaterials.js +30 -0
  75. package/dist/components/Studio/domain/triggerImageRegistry.d.ts +13 -0
  76. package/dist/components/Studio/domain/triggerImageRegistry.js +47 -0
  77. package/dist/components/Studio/domain/useStudioShaderTimeUniforms.d.ts +6 -0
  78. package/dist/components/Studio/domain/useStudioShaderTimeUniforms.js +48 -0
  79. package/dist/components/Studio/domain/useStudioShaderViewportUniforms.d.ts +6 -0
  80. package/dist/components/Studio/domain/useStudioShaderViewportUniforms.js +48 -0
  81. package/dist/components/Studio/domain/viroNodeFactory.d.ts +28 -0
  82. package/dist/components/Studio/domain/viroNodeFactory.js +193 -0
  83. package/dist/components/Studio/index.d.ts +3 -0
  84. package/dist/components/Studio/index.js +7 -0
  85. package/dist/components/Studio/types.d.ts +149 -0
  86. package/dist/components/Studio/types.js +4 -0
  87. package/dist/components/Types/ViroEvents.d.ts +49 -1
  88. package/dist/components/Types/ViroEvents.js +1 -0
  89. package/dist/components/Utilities/VRModuleOpenXR.d.ts +32 -0
  90. package/dist/components/Utilities/VRModuleOpenXR.js +44 -0
  91. package/dist/components/Utilities/VRQuestNavigatorBridge.d.ts +85 -0
  92. package/dist/components/Utilities/VRQuestNavigatorBridge.js +124 -0
  93. package/dist/components/Utilities/ViroPlatform.d.ts +10 -0
  94. package/dist/components/Utilities/ViroPlatform.js +43 -0
  95. package/dist/components/Utilities/ViroVersion.d.ts +1 -1
  96. package/dist/components/Utilities/ViroVersion.js +1 -1
  97. package/dist/components/Utilities/useAnySourceHover.d.ts +36 -0
  98. package/dist/components/Utilities/useAnySourceHover.js +48 -0
  99. package/dist/components/Utilities/useAnySourcePressed.d.ts +37 -0
  100. package/dist/components/Utilities/useAnySourcePressed.js +61 -0
  101. package/dist/components/ViroQuestEntryPoint.d.ts +13 -0
  102. package/dist/components/ViroQuestEntryPoint.js +104 -0
  103. package/dist/components/ViroVRSceneNavigator.d.ts +24 -10
  104. package/dist/components/ViroVRSceneNavigator.js +21 -18
  105. package/dist/components/ViroXRSceneNavigator.d.ts +54 -0
  106. package/dist/components/ViroXRSceneNavigator.js +173 -0
  107. package/dist/components/VisionOS/ViroVisionOSModule.d.ts +65 -0
  108. package/dist/components/VisionOS/ViroVisionOSModule.js +91 -0
  109. package/dist/index.d.ts +15 -2
  110. package/dist/index.js +32 -2
  111. package/dist/plugins/withViro.d.ts +17 -1
  112. package/dist/plugins/withViroAndroid.js +312 -7
  113. package/dist/plugins/withViroIos.js +5 -0
  114. package/dist/plugins/withViroVisionOS.d.ts +24 -0
  115. package/dist/plugins/withViroVisionOS.js +265 -0
  116. package/index.ts +58 -0
  117. package/ios/ViroReact.podspec +13 -4
  118. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreCoreMLSemanticsResources.bundle/Info.plist +0 -0
  119. package/ios/dist/ViroRenderer/ViroKit.framework/ARCoreResources.bundle/Info.plist +0 -0
  120. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSession.h +10 -0
  121. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROARSessioniOS.h +4 -0
  122. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputControllerBase.h +74 -0
  123. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROInputType.h +11 -3
  124. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VROPlatformUtil.h +13 -0
  125. package/ios/dist/ViroRenderer/ViroKit.framework/Headers/VRORenderer.h +8 -0
  126. package/ios/dist/ViroRenderer/ViroKit.framework/Info.plist +0 -0
  127. package/ios/dist/ViroRenderer/ViroKit.framework/Shaders.dat +1 -1
  128. package/ios/dist/ViroRenderer/ViroKit.framework/ViroKit +0 -0
  129. package/ios/dist/ViroRenderer/ViroKit.podspec +5 -0
  130. package/ios/dist/include/VRTARSceneNavigator.h +3 -0
  131. package/ios/dist/include/VRTStudioModule.h +6 -0
  132. package/ios/dist/lib/libViroReact.a +0 -0
  133. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ViroARTrackingReasonConstants = exports.Viro3DSceneNavigator = exports.ViroVRSceneNavigator = exports.ViroVideo = exports.ViroText = exports.ViroSpotLight = exports.ViroSpinner = exports.ViroSphere = exports.ViroSpatialSound = exports.ViroSoundField = exports.ViroSound = exports.ViroAnimatedComponent = exports.ViroAmbientLight = exports.ViroAnimatedImage = exports.Viro360Video = exports.Viro360Image = exports.Viro3DObject = exports.ViroAnimations = exports.ViroSkyBox = exports.ViroSceneNavigator = exports.ViroSurface = exports.ViroScene = exports.ViroQuad = exports.ViroPortalScene = exports.ViroPortal = exports.ViroPolyline = exports.ViroPolygon = exports.ViroParticleEmitter = exports.ViroOrbitCamera = exports.ViroOmniLight = exports.ViroNode = exports.ViroMaterialVideo = exports.ViroARCamera = exports.ViroMaterials = exports.ViroImage = exports.ViroLightingEnvironment = exports.ViroGeometry = exports.ViroFlexView = exports.ViroDirectionalLight = exports.ViroController = exports.ViroCamera = exports.ViroButton = exports.ViroBox = exports.ViroARSceneNavigator = exports.ViroARScene = exports.ViroARPlaneSelector = exports.ViroARPlane = exports.ViroARTrackingTargets = exports.ViroARObjectMarker = exports.ViroARImageMarker = void 0;
4
- exports.VIRO_VERSION = exports.ViroRotateStateTypes = exports.ViroPinchStateTypes = exports.ViroClickStateTypes = exports.gpsToArWorld = exports.latLngToMercator = exports.checkPermissions = exports.requestRequiredPermissions = exports.isARSupportedOnDevice = exports.polarToCartesianActual = exports.polarToCartesian = exports.ViroTrackingStateConstants = exports.ViroRecordingErrorConstants = void 0;
3
+ exports.ViroQuestEntryPoint = exports.ViroXRSceneNavigator = exports.ViroVRSceneNavigator = exports.ViroVideo = exports.ViroText = exports.ViroSpotLight = exports.ViroSpinner = exports.ViroSphere = exports.ViroSpatialSound = exports.ViroSoundField = exports.ViroSound = exports.ViroAnimatedComponent = exports.ViroAmbientLight = exports.ViroAnimatedImage = exports.Viro360Video = exports.Viro360Image = exports.Viro3DObject = exports.ViroAnimations = exports.ViroSkyBox = exports.ViroSceneNavigator = exports.ViroSurface = exports.ViroScene = exports.ViroQuad = exports.ViroPortalScene = exports.ViroPortal = exports.ViroPolyline = exports.ViroPolygon = exports.ViroParticleEmitter = exports.ViroOrbitCamera = exports.ViroOmniLight = exports.ViroNode = exports.ViroMaterialVideo = exports.ViroARCamera = exports.ViroMaterials = exports.ViroImage = exports.ViroLightingEnvironment = exports.ViroGeometry = exports.ViroFlexView = exports.ViroDirectionalLight = exports.ViroController = exports.ViroCamera = exports.ViroButton = exports.ViroBox = exports.ViroARSceneNavigator = exports.ViroARScene = exports.ViroARPlaneSelector = exports.ViroARPlane = exports.ViroARTrackingTargets = exports.ViroARObjectMarker = exports.ViroARImageMarker = void 0;
4
+ exports.exitImmersiveSpace = exports.enterImmersiveSpace = exports.isVisionOS = exports.ViroVisionOSModule = exports.StudioARScene = exports.StudioSceneNavigator = exports.VIRO_VERSION = exports.ViroRotateStateTypes = exports.ViroPinchStateTypes = exports.ViroClickStateTypes = exports.gpsToArWorld = exports.latLngToMercator = exports.checkPermissions = exports.requestRequiredPermissions = exports.isARSupportedOnDevice = exports.polarToCartesianActual = exports.polarToCartesian = exports.ViroTrackingStateConstants = exports.ViroRecordingErrorConstants = exports.ViroARTrackingReasonConstants = exports.useAnySourcePressed = exports.useAnySourceHover = exports.isQuest = exports.hasOpenXRSupport = exports.Viro3DSceneNavigator = exports.exitVRScene = exports.useVRViewTag = exports.VRModuleOpenXR = exports.VRQuestNavigatorBridge = void 0;
5
5
  /**
6
6
  * Copyright (c) 2016-present, Viro Media, Inc.
7
7
  * All rights reserved.
@@ -9,6 +9,14 @@ exports.VIRO_VERSION = exports.ViroRotateStateTypes = exports.ViroPinchStateType
9
9
  */
10
10
  const ViroAnimations_1 = require("./components/Animation/ViroAnimations");
11
11
  Object.defineProperty(exports, "ViroAnimations", { enumerable: true, get: function () { return ViroAnimations_1.ViroAnimations; } });
12
+ const Studio_1 = require("./components/Studio");
13
+ Object.defineProperty(exports, "StudioSceneNavigator", { enumerable: true, get: function () { return Studio_1.StudioSceneNavigator; } });
14
+ Object.defineProperty(exports, "StudioARScene", { enumerable: true, get: function () { return Studio_1.StudioARScene; } });
15
+ const ViroVisionOSModule_1 = require("./components/VisionOS/ViroVisionOSModule");
16
+ Object.defineProperty(exports, "ViroVisionOSModule", { enumerable: true, get: function () { return ViroVisionOSModule_1.ViroVisionOSModule; } });
17
+ Object.defineProperty(exports, "isVisionOS", { enumerable: true, get: function () { return ViroVisionOSModule_1.isVisionOS; } });
18
+ Object.defineProperty(exports, "enterImmersiveSpace", { enumerable: true, get: function () { return ViroVisionOSModule_1.enterImmersiveSpace; } });
19
+ Object.defineProperty(exports, "exitImmersiveSpace", { enumerable: true, get: function () { return ViroVisionOSModule_1.exitImmersiveSpace; } });
12
20
  const Viro3DObject_1 = require("./components/Viro3DObject");
13
21
  Object.defineProperty(exports, "Viro3DObject", { enumerable: true, get: function () { return Viro3DObject_1.Viro3DObject; } });
14
22
  const Viro360Image_1 = require("./components/Viro360Image");
@@ -101,8 +109,17 @@ const ViroVideo_1 = require("./components/ViroVideo");
101
109
  Object.defineProperty(exports, "ViroVideo", { enumerable: true, get: function () { return ViroVideo_1.ViroVideo; } });
102
110
  const ViroVRSceneNavigator_1 = require("./components/ViroVRSceneNavigator");
103
111
  Object.defineProperty(exports, "ViroVRSceneNavigator", { enumerable: true, get: function () { return ViroVRSceneNavigator_1.ViroVRSceneNavigator; } });
112
+ const ViroXRSceneNavigator_1 = require("./components/ViroXRSceneNavigator");
113
+ Object.defineProperty(exports, "ViroXRSceneNavigator", { enumerable: true, get: function () { return ViroXRSceneNavigator_1.ViroXRSceneNavigator; } });
104
114
  const Viro3DSceneNavigator_1 = require("./components/Viro3DSceneNavigator");
105
115
  Object.defineProperty(exports, "Viro3DSceneNavigator", { enumerable: true, get: function () { return Viro3DSceneNavigator_1.Viro3DSceneNavigator; } });
116
+ const ViroPlatform_1 = require("./components/Utilities/ViroPlatform");
117
+ Object.defineProperty(exports, "hasOpenXRSupport", { enumerable: true, get: function () { return ViroPlatform_1.hasOpenXRSupport; } });
118
+ Object.defineProperty(exports, "isQuest", { enumerable: true, get: function () { return ViroPlatform_1.isQuest; } });
119
+ const useAnySourceHover_1 = require("./components/Utilities/useAnySourceHover");
120
+ Object.defineProperty(exports, "useAnySourceHover", { enumerable: true, get: function () { return useAnySourceHover_1.useAnySourceHover; } });
121
+ const useAnySourcePressed_1 = require("./components/Utilities/useAnySourcePressed");
122
+ Object.defineProperty(exports, "useAnySourcePressed", { enumerable: true, get: function () { return useAnySourcePressed_1.useAnySourcePressed; } });
106
123
  const ViroUtils_1 = require("./components/Utilities/ViroUtils");
107
124
  Object.defineProperty(exports, "polarToCartesian", { enumerable: true, get: function () { return ViroUtils_1.polarToCartesian; } });
108
125
  Object.defineProperty(exports, "polarToCartesianActual", { enumerable: true, get: function () { return ViroUtils_1.polarToCartesianActual; } });
@@ -123,3 +140,16 @@ const ViroSceneNavigator_1 = require("./components/ViroSceneNavigator");
123
140
  Object.defineProperty(exports, "ViroSceneNavigator", { enumerable: true, get: function () { return ViroSceneNavigator_1.ViroSceneNavigator; } });
124
141
  const ViroVersion_1 = require("./components/Utilities/ViroVersion");
125
142
  Object.defineProperty(exports, "VIRO_VERSION", { enumerable: true, get: function () { return ViroVersion_1.VIRO_VERSION; } });
143
+ const ViroQuestEntryPoint_1 = require("./components/ViroQuestEntryPoint");
144
+ Object.defineProperty(exports, "ViroQuestEntryPoint", { enumerable: true, get: function () { return ViroQuestEntryPoint_1.ViroQuestEntryPoint; } });
145
+ const VRQuestNavigatorBridge_1 = require("./components/Utilities/VRQuestNavigatorBridge");
146
+ Object.defineProperty(exports, "VRQuestNavigatorBridge", { enumerable: true, get: function () { return VRQuestNavigatorBridge_1.VRQuestNavigatorBridge; } });
147
+ const VRModuleOpenXR_1 = require("./components/Utilities/VRModuleOpenXR");
148
+ Object.defineProperty(exports, "VRModuleOpenXR", { enumerable: true, get: function () { return VRModuleOpenXR_1.VRModuleOpenXR; } });
149
+ Object.defineProperty(exports, "useVRViewTag", { enumerable: true, get: function () { return VRModuleOpenXR_1.useVRViewTag; } });
150
+ Object.defineProperty(exports, "exitVRScene", { enumerable: true, get: function () { return VRModuleOpenXR_1.exitVRScene; } });
151
+ const react_native_1 = require("react-native");
152
+ // Auto-register the Quest VR entry point. VRActivity launches this component
153
+ // as 'VRQuestScene'. Registering here means apps need no manual setup.
154
+ // Apps that need a custom VR root can re-register after importing this package.
155
+ react_native_1.AppRegistry.registerComponent("VRQuestScene", () => ViroQuestEntryPoint_1.ViroQuestEntryPoint);
@@ -1,5 +1,5 @@
1
1
  import { ConfigPlugin } from "@expo/config-plugins";
2
- export type XrMode = "GVR" | "AR" | "OVR_MOBILE";
2
+ export type XrMode = "GVR" | "AR" | "OVR_MOBILE" | "QUEST";
3
3
  /**
4
4
  * Anchor provider type.
5
5
  * - "none": Disabled
@@ -55,6 +55,13 @@ export interface ViroConfigurationOptions {
55
55
  * Written to AndroidManifest as com.reactvision.RVProjectId and to Info.plist as RVProjectId.
56
56
  */
57
57
  rvProjectId?: string;
58
+ /**
59
+ * Override the ReactVision platform base URL.
60
+ * Omit for production. Set to a staging URL for testing.
61
+ *
62
+ * Written to AndroidManifest as com.reactvision.RVEndpoint and to Info.plist as RVEndpoint.
63
+ */
64
+ rvEndpoint?: string;
58
65
  /**
59
66
  * Anchor provider for both cloud anchors and geospatial anchors.
60
67
  * Replaces the deprecated cloudAnchorProvider + geospatialAnchorProvider props.
@@ -132,6 +139,15 @@ export interface ViroConfigurationOptions {
132
139
  };
133
140
  android?: {
134
141
  xRMode?: XrMode[];
142
+ /**
143
+ * Meta Developer Portal App ID (numeric string).
144
+ * Written to AndroidManifest as com.oculus.app_id meta-data.
145
+ * Eliminates the "App Name Unavailable" popup on Meta Quest.
146
+ *
147
+ * Get your App ID from the Meta Developer Portal:
148
+ * https://developer.oculus.com/manage
149
+ */
150
+ questAppId?: string;
135
151
  };
136
152
  }
137
153
  /**
@@ -46,9 +46,9 @@ const withBranchAndroid = (config) => {
46
46
  const viroPlugin = config?.plugins?.find((plugin) => Array.isArray(plugin) && plugin[0] === "@reactvision/react-viro");
47
47
  if (Array.isArray(viroPlugin)) {
48
48
  if (Array.isArray(viroPlugin[1].android?.xRMode)) {
49
- viroPluginConfig = (viroPlugin[1].android?.xRMode).filter((mode) => ["AR", "GVR", "OVR_MOBILE"].includes(mode));
49
+ viroPluginConfig = (viroPlugin[1].android?.xRMode).filter((mode) => ["AR", "GVR", "OVR_MOBILE", "QUEST"].includes(mode));
50
50
  }
51
- else if (["AR", "GVR", "OVR_MOBILE"].includes(viroPlugin[1]?.android?.xRMode)) {
51
+ else if (["AR", "GVR", "OVR_MOBILE", "QUEST"].includes(viroPlugin[1]?.android?.xRMode)) {
52
52
  viroPluginConfig = [viroPlugin[1]?.android.xRMode];
53
53
  }
54
54
  }
@@ -197,6 +197,14 @@ const withViroManifest = (config) => (0, config_plugins_1.withAndroidManifest)(c
197
197
  },
198
198
  });
199
199
  }
200
+ if (pluginOptions.rvEndpoint) {
201
+ contents?.manifest?.application?.[0]["meta-data"]?.push({
202
+ $: {
203
+ "android:name": "com.reactvision.RVEndpoint",
204
+ "android:value": pluginOptions.rvEndpoint,
205
+ },
206
+ });
207
+ }
200
208
  // Add location permissions when geospatial provider is active
201
209
  if (geospatialAnchorProvider === "arcore" || geospatialAnchorProvider === "reactvision") {
202
210
  const existingPermissions = (contents.manifest["uses-permission"] || [])
@@ -281,14 +289,311 @@ const withViroManifest = (config) => (0, config_plugins_1.withAndroidManifest)(c
281
289
  "tools:replace": "required",
282
290
  },
283
291
  });
292
+ // Quest-specific features and permissions — after uses-feature is initialized
293
+ if (viroPluginConfig.includes("QUEST")) {
294
+ contents.manifest["uses-feature"].push({
295
+ $: {
296
+ "android:name": "android.hardware.vr.headtracking",
297
+ "android:required": "true",
298
+ "android:version": "1",
299
+ },
300
+ });
301
+ contents.manifest["uses-feature"].push({
302
+ $: {
303
+ "android:name": "oculus.software.handtracking",
304
+ "android:required": "false",
305
+ },
306
+ });
307
+ // XR_FB_passthrough requires this uses-feature; without it the OpenXR
308
+ // runtime silently strips the extension. required=false so apps that
309
+ // only render fully-virtual scenes still install on Quest 2 etc.
310
+ contents.manifest["uses-feature"].push({
311
+ $: {
312
+ "android:name": "com.oculus.feature.PASSTHROUGH",
313
+ "android:required": "false",
314
+ },
315
+ });
316
+ const existingPermissions = (contents.manifest["uses-permission"] || [])
317
+ .map((p) => p.$?.["android:name"]);
318
+ if (!existingPermissions.includes("com.oculus.permission.HAND_TRACKING")) {
319
+ contents.manifest["uses-permission"].push({
320
+ $: { "android:name": "com.oculus.permission.HAND_TRACKING" },
321
+ });
322
+ }
323
+ if (!existingPermissions.includes("com.oculus.permission.EYE_TRACKING")) {
324
+ contents.manifest["uses-permission"].push({
325
+ $: { "android:name": "com.oculus.permission.EYE_TRACKING" },
326
+ });
327
+ }
328
+ }
284
329
  return newConfig;
285
330
  });
331
+ // ── Quest VRActivity generation ────────────────────────────────────────────────
332
+ /**
333
+ * When QUEST mode is active, generate VRActivity.kt in the app's android source
334
+ * and register it in AndroidManifest with com.oculus.intent.category.VR.
335
+ *
336
+ * VRActivity is a ReactActivity that mounts "VRQuestScene" — a root component
337
+ * the app must register via AppRegistry. Running in a separate Activity with the
338
+ * VR intent category causes Horizon OS to grant exclusive OpenXR display access.
339
+ */
340
+ const withViroQuestActivity = (config, props) => {
341
+ // Read xRMode directly from config.plugins. We cannot rely on the
342
+ // module-level `viroPluginConfig` here: that variable is only updated
343
+ // when `withBranchAndroid`'s withDangerousMod callback runs (mod-apply
344
+ // time), which is *after* this chain-time check executes. On the first
345
+ // prebuild of a new project, viroPluginConfig still holds the default
346
+ // ["AR", "GVR"] when this plugin is composed, and QUEST mods would be
347
+ // silently skipped — no VRActivity.kt and no manifest entry.
348
+ const viroPluginEntry = config?.plugins?.find((plugin) => Array.isArray(plugin) && plugin[0] === "@reactvision/react-viro");
349
+ let xrModes = ["AR", "GVR"];
350
+ if (Array.isArray(viroPluginEntry)) {
351
+ const xrMode = viroPluginEntry[1]?.android?.xRMode;
352
+ if (Array.isArray(xrMode)) {
353
+ xrModes = xrMode.filter((m) => ["AR", "GVR", "OVR_MOBILE", "QUEST"].includes(m));
354
+ }
355
+ else if (typeof xrMode === "string" &&
356
+ ["AR", "GVR", "OVR_MOBILE", "QUEST"].includes(xrMode)) {
357
+ xrModes = [xrMode];
358
+ }
359
+ }
360
+ if (!xrModes.includes("QUEST"))
361
+ return config;
362
+ // 1. Generate VRActivity.kt
363
+ config = (0, config_plugins_1.withDangerousMod)(config, [
364
+ "android",
365
+ async (config) => {
366
+ const packageName = config?.android?.package ?? "";
367
+ const packagePath = packageName.split(".");
368
+ const activityDir = path_1.default.join(config.modRequest.platformProjectRoot, "app", "src", "main", "java", ...packagePath);
369
+ if (!fs_1.default.existsSync(activityDir)) {
370
+ fs_1.default.mkdirSync(activityDir, { recursive: true });
371
+ }
372
+ const activityPath = path_1.default.join(activityDir, "VRActivity.kt");
373
+ // Only write if not already present (preserve manual edits)
374
+ if (!fs_1.default.existsSync(activityPath)) {
375
+ const kotlinContent = `package ${packageName}
376
+
377
+ import android.app.Activity
378
+ import android.app.Application
379
+ import android.os.Bundle
380
+ import android.os.Handler
381
+ import android.os.Looper
382
+ import com.facebook.react.ReactActivity
383
+ import com.facebook.react.ReactActivityDelegate
384
+ import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
385
+ import com.facebook.react.defaults.DefaultReactActivityDelegate
386
+ import com.facebook.react.runtime.ReactHostImpl
387
+
388
+ /**
389
+ * VRActivity — generated by @reactvision/react-viro Expo plugin.
390
+ * Carries com.oculus.intent.category.VR so Horizon OS grants exclusive
391
+ * OpenXR display access. Mounts the "VRQuestScene" React root component.
392
+ * Launch via NativeModules.VRLauncher.launchVRScene().
393
+ *
394
+ * Lifecycle (requires React Native >= 0.83 / Expo >= 55):
395
+ * VRActivity and MainActivity share one ReactHostImpl singleton. On Quest,
396
+ * VRActivity launches into a separate task (FLAG_ACTIVITY_NEW_TASK), so
397
+ * MainActivity.onPause is dispatched *later* via ActivityManager IPC —
398
+ * often after VRActivity.onResume has already promoted the host to RESUMED
399
+ * with currentActivity = VRActivity. When that delayed onPause arrives,
400
+ * the standard delegate calls onHostPause(MainActivity); the identity-
401
+ * mismatch assertion is downgraded to a warning by
402
+ * skipActivityIdentityAssertionOnHostPause (set in VRLauncherModule before
403
+ * startActivity), but the rest of onHostPause runs unconditionally:
404
+ * devSupport is disabled and JavaTimerManager clears its TIMERS_EVENTS
405
+ * callback. Result: timers / requestAnimationFrame stop, Metro Fast Refresh
406
+ * dies.
407
+ *
408
+ * We catch this by listening for that delayed MainActivity.onPause via
409
+ * Application.ActivityLifecycleCallbacks.onActivityPaused(other) and re-
410
+ * promoting the host on the main looper. A Handler.post inside our own
411
+ * onResume would fire too early (the ActivityManager hasn't queued
412
+ * MainActivity.onPause yet), which is why we hook the actual pause event.
413
+ *
414
+ * On exit, VRActivity.onPause runs while currentActivity == this, so the
415
+ * identity assertion passes; MainActivity.onResume then re-promotes via
416
+ * its standard delegate. We do *not* re-promote on a self-pause — the
417
+ * onActivityPaused callback skips when other === this.
418
+ *
419
+ * Mutual exclusion with MainActivity:
420
+ * Quest will happily run MainActivity (panel) and VRActivity (immersive) at
421
+ * the same time if the user taps the app icon in the dock while VR is
422
+ * running. The same Application.ActivityLifecycleCallbacks finishes self
423
+ * when any other Activity in this app resumes — returning the user cleanly
424
+ * to the panel.
425
+ *
426
+ * Surface cleanup on destroy:
427
+ * When VRActivity is destroyed we unload its React surface (reactSurface.stop()
428
+ * in bridgeless arch) so its component tree — including all VRT* components
429
+ * — is unmounted. Without this, the React surface stayed alive in the shared
430
+ * ReactHost across VR re-entries, and on the second/third VR launch each
431
+ * stale surface remounted alongside the fresh one. The C++ scene-graph
432
+ * resolves a click hit to a single viewTag, but multiple VRTComponents
433
+ * claimed it across surfaces — most with a ReactContext bound to a
434
+ * destroyed VRActivity. Click events to the stale ReactContexts crashed in
435
+ * ComponentEventDelegate as IllegalArgumentException SoftExceptions and were
436
+ * silently dropped, so onClick took dozens of presses to register.
437
+ * We still no-op the *delegate* onDestroy() so MainActivity keeps its
438
+ * ReactHost; we only stop the surface explicitly.
439
+ */
440
+ class VRActivity : ReactActivity() {
441
+
442
+ private val mainHandler = Handler(Looper.getMainLooper())
443
+ private var lifecycleCallbacks: Application.ActivityLifecycleCallbacks? = null
444
+
445
+ // Set true on every VRActivity.onResume; consumed by the first
446
+ // onActivityPaused(other) that follows. One-shot — defends against the
447
+ // single MainActivity.onPause that the NEW_TASK ordering schedules late.
448
+ // Without one-shot semantics, every subsequent foreign pause would re-fire
449
+ // ReactHostImpl.onHostResume, which re-fires every LifecycleEventListener
450
+ // (AppState → "active" → ViroXRSceneNavigator's AppState handler calls
451
+ // launchVRScene() again → visible scene flicker).
452
+ private var expectingForeignPause = false
453
+
454
+ override fun getMainComponentName(): String = "VRQuestScene"
455
+
456
+ override fun createReactActivityDelegate(): ReactActivityDelegate =
457
+ object : DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) {
458
+ // VRActivity destruction MUST NOT tear down the shared ReactHost.
459
+ // MainActivity is still alive (and re-foreground after VR exit) and
460
+ // depends on the same ReactContext. The standard delegate's
461
+ // onDestroy forwards to ReactHostImpl.onHostDestroy(VR), which
462
+ // fires LifecycleEventListener.onHostDestroy on every native module
463
+ // — releasing native resources MainActivity still uses. The next
464
+ // Cmd+R reload then runs the JS bundle re-init against a partially
465
+ // destroyed native side and fails with
466
+ // [runtime not ready]: TypeError: Cannot read property
467
+ // 'EventEmitter' of undefined
468
+ // followed by AppRegistryBinding::stopSurface failed and the
469
+ // surface never recovers. By no-op'ing onDestroy here, host
470
+ // destruction is left to MainActivity's standard delegate (the real
471
+ // last activity). The surface itself is stopped explicitly in
472
+ // VRActivity.onDestroy() below.
473
+ override fun onDestroy() {
474
+ // intentionally no-op — see comment above
475
+ }
476
+ }
477
+
478
+ override fun onResume() {
479
+ super.onResume() // delegate.onResume() -> reactHost.onHostResume(this)
480
+ expectingForeignPause = true
481
+ }
482
+
483
+ // onPause not overridden: the default delegate forwards to
484
+ // reactHost.onHostPause(this) while currentActivity matches us, which is
485
+ // what we want.
486
+
487
+ override fun onCreate(savedInstanceState: Bundle?) {
488
+ super.onCreate(savedInstanceState)
489
+ val callbacks = object : Application.ActivityLifecycleCallbacks {
490
+ override fun onActivityResumed(other: Activity) {
491
+ if (other !== this@VRActivity && !isFinishing && !isDestroyed) {
492
+ finish()
493
+ }
494
+ }
495
+ override fun onActivityPaused(other: Activity) {
496
+ if (other === this@VRActivity || isFinishing || isDestroyed) return
497
+ if (!expectingForeignPause) return
498
+ expectingForeignPause = false
499
+ // MainActivity.onPause was dispatched after our onResume (the
500
+ // NEW_TASK race). The standard delegate already called
501
+ // onHostPause(MainActivity), demoting the host to PAUSED and
502
+ // disabling devSupport / clearing JavaTimerManager's frame
503
+ // callback. Re-promote so VR keeps timers + Fast Refresh.
504
+ mainHandler.post {
505
+ if (isFinishing || isDestroyed) return@post
506
+ (reactHost as? ReactHostImpl)?.onHostResume(this@VRActivity)
507
+ }
508
+ }
509
+ override fun onActivityCreated(a: Activity, b: Bundle?) {}
510
+ override fun onActivityStarted(a: Activity) {}
511
+ override fun onActivityStopped(a: Activity) {}
512
+ override fun onActivitySaveInstanceState(a: Activity, b: Bundle) {}
513
+ override fun onActivityDestroyed(a: Activity) {}
514
+ }
515
+ application.registerActivityLifecycleCallbacks(callbacks)
516
+ lifecycleCallbacks = callbacks
517
+ }
518
+
519
+ override fun onDestroy() {
520
+ // Stop the React surface attached to this VRActivity so its component
521
+ // tree unmounts cleanly. In bridgeless arch this calls
522
+ // reactSurface.stop() + reactSurface = null, leaving the shared
523
+ // ReactHost intact for MainActivity. Without this the React tree from
524
+ // each VR session piles up across re-entries and stale VRTComponents
525
+ // intercept hit-tested viewTags with dead ReactContexts, breaking
526
+ // onClick / onHover until you press dozens of times.
527
+ try {
528
+ reactDelegate?.unloadApp()
529
+ } catch (t: Throwable) {
530
+ android.util.Log.w("VRActivity", "reactDelegate.unloadApp() failed", t)
531
+ }
532
+ lifecycleCallbacks?.let { application.unregisterActivityLifecycleCallbacks(it) }
533
+ lifecycleCallbacks = null
534
+ mainHandler.removeCallbacksAndMessages(null)
535
+ super.onDestroy()
536
+ }
537
+ }
538
+ `;
539
+ fs_1.default.writeFileSync(activityPath, kotlinContent, "utf-8");
540
+ }
541
+ return config;
542
+ },
543
+ ]);
544
+ // 2. Add VRActivity to AndroidManifest
545
+ config = (0, config_plugins_1.withAndroidManifest)(config, async (config) => {
546
+ const app = config.modResults.manifest.application?.[0];
547
+ if (!app)
548
+ return config;
549
+ if (!app.activity)
550
+ app.activity = [];
551
+ const alreadyAdded = app.activity.some((a) => a.$?.["android:name"] === ".VRActivity");
552
+ if (!alreadyAdded) {
553
+ app.activity.push({
554
+ $: {
555
+ "android:name": ".VRActivity",
556
+ "android:screenOrientation": "landscape",
557
+ "android:exported": "false",
558
+ "android:configChanges": "keyboard|keyboardHidden|orientation|screenSize|uiMode",
559
+ "android:launchMode": "singleTask",
560
+ },
561
+ "intent-filter": [
562
+ {
563
+ action: [{ $: { "android:name": "android.intent.action.MAIN" } }],
564
+ category: [
565
+ { $: { "android:name": "com.oculus.intent.category.VR" } },
566
+ ],
567
+ },
568
+ ],
569
+ });
570
+ }
571
+ // Inject com.oculus.app_id into <application> for Meta Quest App Name
572
+ const questAppId = props?.android?.questAppId;
573
+ if (questAppId) {
574
+ if (!app["meta-data"])
575
+ app["meta-data"] = [];
576
+ const alreadyHasAppId = app["meta-data"].some((m) => m.$?.["android:name"] === "com.oculus.app_id");
577
+ if (!alreadyHasAppId) {
578
+ app["meta-data"].push({
579
+ $: {
580
+ "android:name": "com.oculus.app_id",
581
+ "android:value": questAppId,
582
+ },
583
+ });
584
+ }
585
+ }
586
+ return config;
587
+ });
588
+ return config;
589
+ };
286
590
  const withViroAndroid = (config, props) => {
287
- (0, config_plugins_1.withPlugins)(config, [[withBranchAndroid, props]]);
288
- withViroProjectBuildGradle(config);
289
- withViroManifest(config);
290
- withViroSettingsGradle(config);
291
- withViroAppBuildGradle(config);
591
+ config = withBranchAndroid(config, props);
592
+ config = withViroProjectBuildGradle(config);
593
+ config = withViroManifest(config);
594
+ config = withViroSettingsGradle(config);
595
+ config = withViroAppBuildGradle(config);
596
+ config = withViroQuestActivity(config, props);
292
597
  return config;
293
598
  };
294
599
  exports.withViroAndroid = withViroAndroid;
@@ -171,6 +171,7 @@ const withDefaultInfoPlist = (config, _props) => {
171
171
  let googleCloudApiKey;
172
172
  let rvApiKey;
173
173
  let rvProjectId;
174
+ let rvEndpoint;
174
175
  let cloudAnchorProvider;
175
176
  let geospatialAnchorProvider;
176
177
  let includeARCore;
@@ -190,6 +191,7 @@ const withDefaultInfoPlist = (config, _props) => {
190
191
  googleCloudApiKey = pluginOptions.googleCloudApiKey;
191
192
  rvApiKey = pluginOptions.rvApiKey;
192
193
  rvProjectId = pluginOptions.rvProjectId;
194
+ rvEndpoint = pluginOptions.rvEndpoint;
193
195
  // Resolve unified provider prop; old props override for backward compat.
194
196
  // Default to "reactvision" only when rvApiKey is present (implies RV intent).
195
197
  const defaultProvider2 = pluginOptions.rvApiKey ? "reactvision" : undefined;
@@ -224,6 +226,9 @@ const withDefaultInfoPlist = (config, _props) => {
224
226
  if (rvProjectId) {
225
227
  config.ios.infoPlist.RVProjectId = rvProjectId;
226
228
  }
229
+ if (rvEndpoint) {
230
+ config.ios.infoPlist.RVEndpoint = rvEndpoint;
231
+ }
227
232
  // Add location permissions for Geospatial API
228
233
  if (geospatialAnchorProvider === "arcore" || geospatialAnchorProvider === "reactvision" || includeARCore === true) {
229
234
  config.ios.infoPlist.NSLocationWhenInUseUsageDescription =
@@ -0,0 +1,24 @@
1
+ /**
2
+ * withViroVisionOS.ts
3
+ *
4
+ * Expo config plugin that adds a visionOS target to your app's Xcode project.
5
+ *
6
+ * What it does:
7
+ * 1. Writes the SwiftUI App entry point (ViroVisionApp.swift) into the target folder
8
+ * 2. Adds a visionOS pod target to the Podfile
9
+ * 3. Creates a visionOS Xcode target in the .pbxproj with correct SDK + build settings
10
+ *
11
+ * Usage in app.json:
12
+ * {
13
+ * "plugins": [
14
+ * ["@reactvision/react-viro", { ... }],
15
+ * "@reactvision/react-viro/plugins/withViroVisionOS"
16
+ * ]
17
+ * }
18
+ *
19
+ * After running `expo prebuild`, run `pod install` in ios/ then build the
20
+ * <AppName>Vision target in Xcode targeting the visionOS simulator.
21
+ */
22
+ import { ConfigPlugin } from "@expo/config-plugins";
23
+ export declare const withViroVisionOS: ConfigPlugin;
24
+ export default withViroVisionOS;