Skip to content

Commit 190295a

Browse files
Merge develop → main (AR stability + depth estimation for non-LiDAR) (#428)
* chore: fix ios crash, add depth-based ar hit * fix: hitResultId not available issue * chore: made non-lidar to fallback to mono, simplify integration and bug fixes * feat: integrate depth changes from virocore * feat: include latest virocore * chore: implement latest virocore * fix: remove debug shader from production code --------- Co-authored-by: Benito Mota <benmotanoti@gmail.com>
1 parent d03c609 commit 190295a

File tree

64 files changed

+4221
-614
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+4221
-614
lines changed
8.5 KB
Binary file not shown.

android/viro_bridge/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ dependencies {
114114
implementation 'androidx.appcompat:appcompat:1.0.0'
115115
testImplementation 'junit:junit:4.12'
116116
implementation 'com.google.ar:core:1.43.0'
117+
// Required for ARCore Geospatial API
118+
implementation 'com.google.android.gms:play-services-location:21.0.1'
117119
implementation project(':gvr_common')
118120
implementation project(':viro_renderer')
119121
implementation 'com.google.android.exoplayer:exoplayer:2.19.1'

android/viro_bridge/src/main/java/com/viromedia/bridge/component/VRTARSceneNavigator.java

Lines changed: 84 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import com.facebook.react.bridge.Arguments;
3131
import com.facebook.react.bridge.WritableArray;
3232
import com.facebook.react.bridge.WritableMap;
33+
import com.facebook.react.uimanager.events.RCTEventEmitter;
3334
import com.viro.core.ARAnchor;
3435
import com.viro.core.ARNode;
3536
import com.viro.core.ARScene;
@@ -60,6 +61,10 @@ public class VRTARSceneNavigator extends VRT3DSceneNavigator {
6061
private boolean mGeospatialModeEnabled = false;
6162
private boolean mNeedsGeospatialModeToggle = false;
6263

64+
// Track if we were detached from window (for tab switching detection)
65+
// This is separate from VRTComponent's mDetached which tracks React tree detachment
66+
private boolean mWasDetachedFromWindow = false;
67+
6368
private static class StartupListenerARCore implements ViroViewARCore.StartupListener {
6469

6570
private WeakReference<VRTARSceneNavigator> mNavigator;
@@ -178,36 +183,101 @@ public void resetARSession() {
178183
// No-op for now.
179184
}
180185

186+
@Override
187+
protected void onAttachedToWindow() {
188+
android.util.Log.i(TAG, "=== onAttachedToWindow START ===");
189+
android.util.Log.i(TAG, " mWasDetachedFromWindow: " + mWasDetachedFromWindow);
190+
android.util.Log.i(TAG, " mViroView: " + (mViroView != null ? "NOT NULL" : "NULL"));
191+
192+
// Detect tab switch scenario: we were detached before and ViroView was disposed
193+
if (mWasDetachedFromWindow && mViroView == null) {
194+
android.util.Log.i(TAG, " TAB SWITCH DETECTED - Emitting event to React");
195+
196+
// Emit event to React side to trigger remount
197+
emitTabSwitchEvent();
198+
199+
// Reset flag
200+
mWasDetachedFromWindow = false;
201+
202+
// Don't call super - React will remount us with a fresh key
203+
android.util.Log.i(TAG, "=== onAttachedToWindow END (waiting for React remount) ===");
204+
return;
205+
}
206+
207+
// Normal attach (first time or after resume without disposal)
208+
super.onAttachedToWindow();
209+
210+
// Re-enable rotation listener when view is reattached
211+
if (mRotationListener != null) {
212+
mRotationListener.enable();
213+
android.util.Log.i(TAG, " Rotation listener enabled");
214+
}
215+
216+
android.util.Log.i(TAG, "=== onAttachedToWindow END (normal) ===");
217+
}
218+
181219
@Override
182220
protected void onDetachedFromWindow() {
183-
super.onDetachedFromWindow();
221+
android.util.Log.i(TAG, "=== onDetachedFromWindow START ===");
222+
android.util.Log.i(TAG, " mViroView: " + (mViroView != null ? "NOT NULL" : "NULL"));
223+
224+
// Mark that we were detached - used to detect tab switches
225+
mWasDetachedFromWindow = true;
226+
227+
// Disable rotation listener
184228
if (mRotationListener != null) {
185229
mRotationListener.disable();
230+
android.util.Log.i(TAG, " Rotation listener disabled");
186231
}
232+
233+
// Pause AR session before disposal
234+
ViroViewARCore arView = getARView();
235+
if (arView != null) {
236+
android.app.Activity activity = mReactContext.getCurrentActivity();
237+
if (activity != null) {
238+
android.util.Log.i(TAG, " Pausing AR session");
239+
arView.onActivityPaused(activity);
240+
arView.onActivityStopped(activity);
241+
}
242+
}
243+
244+
// Call parent to dispose ViroView normally
245+
// This is correct for both tab switching and component unmount
246+
super.onDetachedFromWindow();
247+
248+
android.util.Log.i(TAG, "=== onDetachedFromWindow END ===");
187249
}
188250

189251
/**
190-
* Explicitly dispose of AR resources. Called from componentWillUnmount to ensure
191-
* proper cleanup even if onDetachedFromWindow is delayed or not called.
192-
* This method can be called multiple times safely.
252+
* Emit an event to the React side indicating a tab switch was detected.
253+
* The React component will listen for this event and trigger a remount.
193254
*/
255+
private void emitTabSwitchEvent() {
256+
WritableMap event = Arguments.createMap();
257+
event.putString("type", "tabSwitch");
258+
259+
try {
260+
mReactContext
261+
.getJSModule(RCTEventEmitter.class)
262+
.receiveEvent(getId(), "onTabSwitch", event);
263+
android.util.Log.i(TAG, " Tab switch event emitted to React");
264+
} catch (Exception e) {
265+
android.util.Log.e(TAG, " Failed to emit tab switch event: " + e.getMessage());
266+
}
267+
}
268+
194269
public void dispose() {
270+
// Clear the window detachment flag since this is a permanent disposal
271+
mWasDetachedFromWindow = false;
272+
195273
// Disable rotation listener
196274
if (mRotationListener != null) {
197275
mRotationListener.disable();
198276
mRotationListener = null;
199277
}
200278

201-
// Get AR view and pause the session
202-
ViroViewARCore arView = getARView();
203-
if (arView != null) {
204-
// Pause the AR session to release camera and other resources
205-
arView.onActivityPaused(null);
206-
}
207-
208-
// Trigger parent class cleanup which handles scene teardown and ViroView disposal
209-
// This is the same logic as onDetachedFromWindow in VRT3DSceneNavigator
210-
// but can be called proactively from React Native
279+
// The parent's onDetachedFromWindow() will be called by the view system
280+
// when the component is actually removed, so we don't need to call it here
211281
}
212282

213283
public void setAutoFocusEnabled(boolean enabled) {

android/viro_bridge/src/main/java/com/viromedia/bridge/component/VRTARSceneNavigatorManager.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,12 @@
2525

2626
import com.facebook.react.bridge.ReactApplicationContext;
2727
import com.facebook.react.bridge.ReadableMap;
28+
import com.facebook.react.common.MapBuilder;
2829
import com.facebook.react.uimanager.ThemedReactContext;
2930
import com.facebook.react.uimanager.annotations.ReactProp;
3031

32+
import java.util.Map;
33+
3134
/**
3235
* ARSceneNavigatorManager for building a {@link VRTARSceneNavigator}
3336
* corresponding to the ViroARNavigator.js control.
@@ -113,4 +116,18 @@ public void setWorldMeshEnabled(VRTARSceneNavigator navigator, boolean enabled)
113116
public void setWorldMeshConfig(VRTARSceneNavigator navigator, ReadableMap config) {
114117
navigator.setWorldMeshConfig(config);
115118
}
119+
120+
@Override
121+
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
122+
Map<String, Object> events = super.getExportedCustomDirectEventTypeConstants();
123+
if (events == null) {
124+
events = MapBuilder.newHashMap();
125+
}
126+
127+
// Export the onTabSwitch event so React can listen to it
128+
// This event is emitted when a tab switch is detected (window reattach after detach)
129+
events.put("onTabSwitch", MapBuilder.of("registrationName", "onTabSwitch"));
130+
131+
return events;
132+
}
116133
}

android/viro_bridge/src/main/java/com/viromedia/bridge/component/node/control/VRTParticleEmitter.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -423,27 +423,49 @@ private void updateSpawnModifier(){
423423
for (int i = 0; i < burstArray.size(); i++) {
424424
ReadableMap burstmap = burstArray.getMap(i);
425425

426+
// Validate required fields
427+
if (!burstmap.hasKey("min") || !burstmap.hasKey("max")) {
428+
onError("Viro: emissionBurst[" + i + "] is missing required 'min' and/or 'max' fields, skipping.");
429+
continue;
430+
}
431+
426432
ParticleEmitter.Factor referenceFactor;
427433
float valueStart;
428434
float coolPeriod;
429435
int min, max;
430436
int cycles;
437+
438+
// Handle time-based or distance-based burst
431439
if (burstmap.hasKey("time")) {
432440
referenceFactor = ParticleEmitter.Factor.TIME;
433441
valueStart = (float) burstmap.getDouble("time");
442+
// cooldownPeriod is optional, defaults to 0
434443
coolPeriod = (float) (burstmap.hasKey("cooldownPeriod") ? burstmap.getDouble("cooldownPeriod") : 0);
435444
} else if (burstmap.hasKey("distance")) {
436445
referenceFactor = ParticleEmitter.Factor.DISTANCE;
437446
valueStart = (float) burstmap.getDouble("distance");
447+
// cooldownDistance is optional, defaults to 0
438448
coolPeriod = (float) (burstmap.hasKey("cooldownDistance") ? burstmap.getDouble("cooldownDistance") : 0);
439449
} else {
440-
onError("Invalid Burst parameters provided!");
441-
return;
450+
onError("Viro: emissionBurst[" + i + "] must specify either 'time' or 'distance', skipping.");
451+
continue;
442452
}
443453

444-
min = burstmap.hasKey("min") ? burstmap.getInt("min") : 0;
445-
max = burstmap.hasKey("max") ? burstmap.getInt("max") : 0;
446-
cycles = burstmap.hasKey("cycles") ? burstmap.getInt("cycles") : 0;
454+
// Get min/max particle count
455+
min = burstmap.getInt("min");
456+
max = burstmap.getInt("max");
457+
458+
// Validate min/max range
459+
if (min < 0 || max < 0 || min > max) {
460+
onError("Viro: emissionBurst[" + i + "] has invalid min/max values (min: " + min + ", max: " + max + "). Min and max must be >= 0 and min <= max, skipping.");
461+
continue;
462+
}
463+
464+
// cycles is optional, defaults to 1
465+
cycles = burstmap.hasKey("cycles") ? burstmap.getInt("cycles") : 1;
466+
if (cycles <= 0) {
467+
cycles = 1; // Ensure at least 1 cycle
468+
}
447469

448470
ParticleEmitter.EmissionBurst burst = new ParticleEmitter.EmissionBurst(referenceFactor,
449471
valueStart, min, max, cycles, coolPeriod);

0 commit comments

Comments
 (0)