Skip to content

Commit 6674a4f

Browse files
authored
fix: handle navigation listeners on NavigationProvider mount and unmount (#294)
- Initialized navigation listeners on NavigationProvider mount to avoid cases where the native layer sends messages without corresponding listener mappings. - Keep navigation listeners registered even when NavigationProvider is disposed. - Make Android navigation event handling more reliable by re-registering handlers on app lifecycle events; keeping them up to date with react context all the time.
1 parent aa56cbe commit 6674a4f

File tree

3 files changed

+180
-117
lines changed

3 files changed

+180
-117
lines changed

android/src/main/java/com/google/android/react/navsdk/NavModule.java

Lines changed: 167 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import androidx.lifecycle.Observer;
2020
import com.facebook.react.bridge.Arguments;
2121
import com.facebook.react.bridge.CatalystInstance;
22+
import com.facebook.react.bridge.LifecycleEventListener;
2223
import com.facebook.react.bridge.NativeArray;
2324
import com.facebook.react.bridge.Promise;
2425
import com.facebook.react.bridge.ReactApplicationContext;
@@ -58,7 +59,8 @@
5859
* This exposes a series of methods that can be called diretly from the React Native code. They have
5960
* been implemented using promises as it's not recommended for them to be synchronous.
6061
*/
61-
public class NavModule extends ReactContextBaseJavaModule implements INavigationCallback {
62+
public class NavModule extends ReactContextBaseJavaModule
63+
implements INavigationCallback, LifecycleEventListener {
6264
public static final String REACT_CLASS = "NavModule";
6365
private static final String TAG = "NavModule";
6466
private static NavModule instance;
@@ -73,6 +75,12 @@ public class NavModule extends ReactContextBaseJavaModule implements INavigation
7375
private final CopyOnWriteArrayList<NavigationReadyListener> mNavigationReadyListeners =
7476
new CopyOnWriteArrayList<>();
7577
private boolean mIsListeningRoadSnappedLocation = false;
78+
private LocationListener mLocationListener;
79+
private Navigator.ArrivalListener mArrivalListener;
80+
private Navigator.RouteChangedListener mRouteChangedListener;
81+
private Navigator.TrafficUpdatedListener mTrafficUpdatedListener;
82+
private Navigator.ReroutingListener mReroutingListener;
83+
private Navigator.RemainingTimeOrDistanceChangedListener mRemainingTimeOrDistanceChangedListener;
7684

7785
private HashMap<String, Object> tocParamsMap;
7886
private @Navigator.TaskRemovedBehavior int taskRemovedBehaviour;
@@ -87,19 +95,22 @@ public interface NavigationReadyListener {
8795

8896
public NavModule(ReactApplicationContext reactContext, NavViewManager navViewManager) {
8997
super(reactContext);
90-
this.reactContext = reactContext;
91-
mNavViewManager = navViewManager;
92-
instance = this;
98+
setReactContext(reactContext);
99+
setViewManager(navViewManager);
93100
if (moduleReadyListener != null) {
94101
moduleReadyListener.onModuleReady();
95102
}
96103
}
97104

98-
public static void setModuleReadyListener(ModuleReadyListener listener) {
99-
moduleReadyListener = listener;
100-
if (instance != null && moduleReadyListener != null) {
101-
moduleReadyListener.onModuleReady();
105+
public static synchronized NavModule getInstance(
106+
ReactApplicationContext reactContext, NavViewManager navViewManager) {
107+
if (instance == null) {
108+
instance = new NavModule(reactContext, navViewManager);
109+
} else {
110+
instance.setReactContext(reactContext);
111+
instance.setViewManager(navViewManager);
102112
}
113+
return instance;
103114
}
104115

105116
public static synchronized NavModule getInstance() {
@@ -109,6 +120,22 @@ public static synchronized NavModule getInstance() {
109120
return instance;
110121
}
111122

123+
public void setReactContext(ReactApplicationContext reactContext) {
124+
this.reactContext = reactContext;
125+
this.reactContext.addLifecycleEventListener(this);
126+
}
127+
128+
public void setViewManager(NavViewManager navViewManager) {
129+
mNavViewManager = navViewManager;
130+
}
131+
132+
public static void setModuleReadyListener(ModuleReadyListener listener) {
133+
moduleReadyListener = listener;
134+
if (instance != null && moduleReadyListener != null) {
135+
moduleReadyListener.onModuleReady();
136+
}
137+
}
138+
112139
public Navigator getNavigator() {
113140
return mNavigator;
114141
}
@@ -124,85 +151,10 @@ public Map<String, Object> getConstants() {
124151
return constants;
125152
}
126153

127-
private Navigator.ArrivalListener mArrivalListener =
128-
new Navigator.ArrivalListener() {
129-
@Override
130-
public void onArrival(ArrivalEvent arrivalEvent) {
131-
WritableMap map = Arguments.createMap();
132-
map.putMap(
133-
"waypoint", ObjectTranslationUtil.getMapFromWaypoint(arrivalEvent.getWaypoint()));
134-
map.putBoolean("isFinalDestination", arrivalEvent.isFinalDestination());
135-
136-
WritableNativeArray params = new WritableNativeArray();
137-
params.pushMap(map);
138-
139-
sendCommandToReactNative("onArrival", params);
140-
}
141-
};
142-
143-
private LocationListener mLocationListener =
144-
new LocationListener() {
145-
@Override
146-
public void onLocationChanged(final Location location) {
147-
WritableNativeArray params = new WritableNativeArray();
148-
params.pushMap(ObjectTranslationUtil.getMapFromLocation(location));
149-
150-
sendCommandToReactNative("onLocationChanged", params);
151-
}
152-
153-
@Override
154-
public void onRawLocationUpdate(final Location location) {
155-
WritableNativeArray params = new WritableNativeArray();
156-
params.pushMap(ObjectTranslationUtil.getMapFromLocation(location));
157-
158-
sendCommandToReactNative("onRawLocationChanged", params);
159-
}
160-
};
161-
162-
private Navigator.RouteChangedListener mRouteChangedListener =
163-
new Navigator.RouteChangedListener() {
164-
@Override
165-
public void onRouteChanged() {
166-
sendCommandToReactNative("onRouteChanged", (NativeArray) null);
167-
}
168-
};
169-
170-
private Navigator.TrafficUpdatedListener mTrafficUpdatedListener =
171-
new Navigator.TrafficUpdatedListener() {
172-
@Override
173-
public void onTrafficUpdated() {
174-
sendCommandToReactNative("onTrafficUpdated", (NativeArray) null);
175-
}
176-
};
177-
178-
private Navigator.ReroutingListener mReroutingListener =
179-
new Navigator.ReroutingListener() {
180-
@Override
181-
public void onReroutingRequestedByOffRoute() {
182-
sendCommandToReactNative("onReroutingRequestedByOffRoute", (NativeArray) null);
183-
}
184-
};
185-
186-
private Navigator.RemainingTimeOrDistanceChangedListener mRemainingTimeOrDistanceChangedListener =
187-
new Navigator.RemainingTimeOrDistanceChangedListener() {
188-
@Override
189-
public void onRemainingTimeOrDistanceChanged() {
190-
sendCommandToReactNative("onRemainingTimeOrDistanceChanged", (NativeArray) null);
191-
}
192-
};
193-
194154
@ReactMethod
195155
private void cleanup() {
196-
if (mIsListeningRoadSnappedLocation) {
197-
mRoadSnappedLocationProvider.removeLocationListener(mLocationListener);
198-
}
199-
mNavigator.unregisterServiceForNavUpdates();
200-
mNavigator.removeArrivalListener(mArrivalListener);
201-
mNavigator.removeReroutingListener(mReroutingListener);
202-
mNavigator.removeRouteChangedListener(mRouteChangedListener);
203-
mNavigator.removeTrafficUpdatedListener(mTrafficUpdatedListener);
204-
mNavigator.removeRemainingTimeOrDistanceChangedListener(
205-
mRemainingTimeOrDistanceChangedListener);
156+
stopUpdatingLocation();
157+
removeNavigationListeners();
206158
mWaypoints.clear();
207159

208160
for (NavigationReadyListener listener : mNavigationReadyListeners) {
@@ -276,8 +228,12 @@ public void onNavigatorReady(Navigator navigator) {
276228
// Keep a reference to the Navigator (used to configure and start nav)
277229
mNavigator = navigator;
278230
mNavigator.setTaskRemovedBehavior(taskRemovedBehaviour);
279-
mRoadSnappedLocationProvider =
280-
NavigationApi.getRoadSnappedLocationProvider(getCurrentActivity().getApplication());
231+
if (mRoadSnappedLocationProvider == null) {
232+
mRoadSnappedLocationProvider =
233+
NavigationApi.getRoadSnappedLocationProvider(
234+
getCurrentActivity().getApplication());
235+
}
236+
registerNavigationListeners();
281237
onNavigationReady();
282238
}
283239

@@ -330,21 +286,80 @@ public void setTurnByTurnLoggingEnabled(boolean isEnabled) {
330286
* navigation events occur (e.g. the driver's route changes or the destination is reached).
331287
*/
332288
private void registerNavigationListeners() {
289+
removeNavigationListeners();
290+
291+
mArrivalListener =
292+
new Navigator.ArrivalListener() {
293+
@Override
294+
public void onArrival(ArrivalEvent arrivalEvent) {
295+
WritableMap map = Arguments.createMap();
296+
map.putMap(
297+
"waypoint", ObjectTranslationUtil.getMapFromWaypoint(arrivalEvent.getWaypoint()));
298+
map.putBoolean("isFinalDestination", arrivalEvent.isFinalDestination());
299+
300+
WritableNativeArray params = new WritableNativeArray();
301+
params.pushMap(map);
302+
303+
sendCommandToReactNative("onArrival", params);
304+
}
305+
};
333306
mNavigator.addArrivalListener(mArrivalListener);
307+
308+
mRouteChangedListener =
309+
new Navigator.RouteChangedListener() {
310+
@Override
311+
public void onRouteChanged() {
312+
sendCommandToReactNative("onRouteChanged", (NativeArray) null);
313+
}
314+
};
334315
mNavigator.addRouteChangedListener(mRouteChangedListener);
316+
317+
mTrafficUpdatedListener =
318+
new Navigator.TrafficUpdatedListener() {
319+
@Override
320+
public void onTrafficUpdated() {
321+
sendCommandToReactNative("onTrafficUpdated", (NativeArray) null);
322+
}
323+
};
335324
mNavigator.addTrafficUpdatedListener(mTrafficUpdatedListener);
325+
326+
mReroutingListener =
327+
new Navigator.ReroutingListener() {
328+
@Override
329+
public void onReroutingRequestedByOffRoute() {
330+
sendCommandToReactNative("onReroutingRequestedByOffRoute", (NativeArray) null);
331+
}
332+
};
336333
mNavigator.addReroutingListener(mReroutingListener);
334+
335+
mRemainingTimeOrDistanceChangedListener =
336+
new Navigator.RemainingTimeOrDistanceChangedListener() {
337+
@Override
338+
public void onRemainingTimeOrDistanceChanged() {
339+
sendCommandToReactNative("onRemainingTimeOrDistanceChanged", (NativeArray) null);
340+
}
341+
};
337342
mNavigator.addRemainingTimeOrDistanceChangedListener(
338343
0, 0, mRemainingTimeOrDistanceChangedListener);
339344
}
340345

341346
private void removeNavigationListeners() {
342-
mNavigator.removeArrivalListener(mArrivalListener);
343-
mNavigator.removeRouteChangedListener(mRouteChangedListener);
344-
mNavigator.removeTrafficUpdatedListener(mTrafficUpdatedListener);
345-
mNavigator.removeReroutingListener(mReroutingListener);
346-
mNavigator.removeRemainingTimeOrDistanceChangedListener(
347-
mRemainingTimeOrDistanceChangedListener);
347+
if (mArrivalListener != null) {
348+
mNavigator.removeArrivalListener(mArrivalListener);
349+
}
350+
if (mRouteChangedListener != null) {
351+
mNavigator.removeRouteChangedListener(mRouteChangedListener);
352+
}
353+
if (mTrafficUpdatedListener != null) {
354+
mNavigator.removeTrafficUpdatedListener(mTrafficUpdatedListener);
355+
}
356+
if (mReroutingListener != null) {
357+
mNavigator.removeReroutingListener(mReroutingListener);
358+
}
359+
if (mRemainingTimeOrDistanceChangedListener != null) {
360+
mNavigator.removeRemainingTimeOrDistanceChangedListener(
361+
mRemainingTimeOrDistanceChangedListener);
362+
}
348363
}
349364

350365
private void createWaypoint(Map map) {
@@ -468,14 +483,6 @@ private void setOnResultListener(IRouteStatusResult listener) {
468483
@Override
469484
public void onResult(Navigator.RouteStatus code) {
470485
listener.onResult(code);
471-
switch (code) {
472-
case OK:
473-
removeNavigationListeners();
474-
registerNavigationListeners();
475-
break;
476-
default:
477-
break;
478-
}
479486
}
480487
});
481488
}
@@ -725,14 +732,54 @@ public void resetTermsAccepted() {
725732

726733
@ReactMethod
727734
public void startUpdatingLocation() {
728-
mRoadSnappedLocationProvider.addLocationListener(mLocationListener);
735+
registerLocationListener();
729736
mIsListeningRoadSnappedLocation = true;
730737
}
731738

732739
@ReactMethod
733740
public void stopUpdatingLocation() {
734741
mIsListeningRoadSnappedLocation = false;
735-
mRoadSnappedLocationProvider.removeLocationListener(mLocationListener);
742+
removeLocationListener();
743+
}
744+
745+
private void registerLocationListener() {
746+
// Unregister existing location listener if available.
747+
removeLocationListener();
748+
749+
if (mRoadSnappedLocationProvider != null) {
750+
mLocationListener =
751+
new LocationListener() {
752+
@Override
753+
public void onLocationChanged(final Location location) {
754+
if (mIsListeningRoadSnappedLocation) {
755+
WritableNativeArray params = new WritableNativeArray();
756+
params.pushMap(ObjectTranslationUtil.getMapFromLocation(location));
757+
758+
sendCommandToReactNative("onLocationChanged", params);
759+
}
760+
}
761+
762+
@Override
763+
public void onRawLocationUpdate(final Location location) {
764+
if (mIsListeningRoadSnappedLocation) {
765+
WritableNativeArray params = new WritableNativeArray();
766+
params.pushMap(ObjectTranslationUtil.getMapFromLocation(location));
767+
768+
sendCommandToReactNative("onRawLocationChanged", params);
769+
}
770+
}
771+
};
772+
773+
mRoadSnappedLocationProvider.resetFreeNav();
774+
mRoadSnappedLocationProvider.addLocationListener(mLocationListener);
775+
}
776+
}
777+
778+
private void removeLocationListener() {
779+
if (mRoadSnappedLocationProvider != null && mLocationListener != null) {
780+
mRoadSnappedLocationProvider.removeLocationListener(mLocationListener);
781+
mLocationListener = null;
782+
}
736783
}
737784

738785
private void showNavInfo(NavInfo navInfo) {
@@ -781,6 +828,23 @@ public boolean canOverrideExistingModule() {
781828
return true;
782829
}
783830

831+
@Override
832+
public void onHostResume() {
833+
// Re-register listeners on resume.
834+
if (mNavigator != null) {
835+
registerNavigationListeners();
836+
if (mIsListeningRoadSnappedLocation) {
837+
registerLocationListener();
838+
}
839+
}
840+
}
841+
842+
@Override
843+
public void onHostPause() {}
844+
845+
@Override
846+
public void onHostDestroy() {}
847+
784848
private interface IRouteStatusResult {
785849
void onResult(Navigator.RouteStatus code);
786850
}

android/src/main/java/com/google/android/react/navsdk/Package.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public List<ViewManager> createViewManagers(ReactApplicationContext reactContext
3434
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
3535
List<NativeModule> modules = new ArrayList<>();
3636
NavViewManager viewManager = NavViewManager.getInstance(reactContext);
37-
modules.add(new NavModule(reactContext, viewManager));
37+
modules.add(NavModule.getInstance(reactContext, viewManager));
3838
modules.add(new NavAutoModule(reactContext));
3939
modules.add(new NavViewModule(reactContext, viewManager));
4040

0 commit comments

Comments
 (0)