Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,15 @@ You can also add a bare `MapView` that works as a normal map view without naviga
/>
```

### Control light and dark modes

Use the `mapColorScheme` prop on both `NavigationView` and `MapView` to force the map tiles into light, dark, or system-following mode.

For the navigation UI, pass the `navigationNightMode` prop to `NavigationView` to configure the initial lighting mode for navigation session.

> [!NOTE]
> When navigation UI is enabled, `mapColorScheme` does not affect the view styling. To control the style of the navigation UI, use the `navigationNightMode` prop on `NavigationView` instead.

### Requesting and handling permissions

The Google Navigation SDK React Native library offers functionalities that necessitate specific permissions from the mobile operating system. These include, but are not limited to, location services, background execution, and receiving background location updates.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.CameraPerspective;
import com.google.android.gms.maps.model.MapColorScheme;
import com.google.android.libraries.navigation.AlternateRoutesStrategy;
import com.google.android.libraries.navigation.ForceNightMode;
import com.google.android.libraries.navigation.Navigator;
Expand Down Expand Up @@ -96,4 +97,15 @@ public static CustomTypes.MapViewType getMapViewTypeFromJsValue(int jsValue) {
default -> throw new IllegalStateException("Unexpected MapViewType value: " + jsValue);
};
}

public static @MapColorScheme int getMapColorSchemeFromJsValue(int jsValue) {
switch (jsValue) {
case 1:
return MapColorScheme.LIGHT;
case 2:
return MapColorScheme.DARK;
default:
return MapColorScheme.FOLLOW_SYSTEM;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import android.view.View;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.MapColorScheme;

public interface IMapViewFragment {
MapViewController getMapController();
Expand All @@ -23,6 +24,8 @@ public interface IMapViewFragment {

GoogleMap getGoogleMap();

void setMapColorScheme(@MapColorScheme int colorScheme);

// Fragment
boolean isAdded();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package com.google.android.react.navsdk;

import com.google.android.libraries.navigation.ForceNightMode;
import com.google.android.libraries.navigation.StylingOptions;

public interface INavViewFragment extends IMapViewFragment {
Expand All @@ -34,7 +35,7 @@ public interface INavViewFragment extends IMapViewFragment {

void showRouteOverview();

void setNightModeOption(int jsValue);
void setNightModeOption(@ForceNightMode int nightModeOverride);

void setReportIncidentButtonEnabled(boolean enabled);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.GroundOverlayOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapColorScheme;
import com.google.android.gms.maps.model.MapStyleOptions;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
Expand Down Expand Up @@ -513,6 +514,14 @@ public void setMapType(int jsValue) {
mGoogleMap.setMapType(EnumTranslationUtil.getMapTypeFromJsValue(jsValue));
}

public void setColorScheme(@MapColorScheme int mapColorScheme) {
if (mGoogleMap == null) {
return;
}

mGoogleMap.setMapColorScheme(mapColorScheme);
}

public void clearMapView() {
if (mGoogleMap == null) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapColorScheme;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.Polyline;
Expand All @@ -46,6 +47,7 @@ public class MapViewFragment extends SupportMapFragment
private ReactApplicationContext reactContext;
private GoogleMap mGoogleMap;
private MapViewController mMapViewController;
private @MapColorScheme int mapColorScheme = MapColorScheme.FOLLOW_SYSTEM;

public static MapViewFragment newInstance(
ReactApplicationContext reactContext, int viewTag, @NonNull GoogleMapOptions mapOptions) {
Expand Down Expand Up @@ -74,6 +76,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat

// Setup map listeners with the provided callback
mMapViewController.setupMapListeners(MapViewFragment.this);
applyMapColorSchemeToMap();

emitEvent("onMapReady", null);

Expand Down Expand Up @@ -137,6 +140,18 @@ public GoogleMap getGoogleMap() {
return mGoogleMap;
}

@Override
public void setMapColorScheme(@MapColorScheme int mapColorScheme) {
this.mapColorScheme = mapColorScheme;
applyMapColorSchemeToMap();
}

private void applyMapColorSchemeToMap() {
if (mMapViewController != null) {
mMapViewController.setColorScheme(mapColorScheme);
}
}

private void emitEvent(String eventName, @Nullable WritableMap data) {
if (reactContext != null) {
EventDispatcher dispatcher =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.GroundOverlay;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MapColorScheme;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.Polygon;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.libraries.navigation.ForceNightMode;
import com.google.android.libraries.navigation.NavigationView;
import com.google.android.libraries.navigation.PromptVisibilityChangedListener;
import com.google.android.libraries.navigation.StylingOptions;
Expand All @@ -49,6 +51,8 @@ public class NavViewFragment extends SupportNavigationFragment
private MapViewController mMapViewController;
private GoogleMap mGoogleMap;
private StylingOptions mStylingOptions;
private @MapColorScheme int mapColorScheme = MapColorScheme.FOLLOW_SYSTEM;
private @ForceNightMode int nightModeOverride = ForceNightMode.AUTO;

public static NavViewFragment newInstance(
ReactApplicationContext reactContext, int viewTag, @NonNull GoogleMapOptions mapOptions) {
Expand Down Expand Up @@ -79,6 +83,8 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat

// Setup map listeners with the provided callback
mMapViewController.setupMapListeners(NavViewFragment.this);
applyMapColorSchemeToMap();
applyNightModePreference();

emitEvent("onMapReady", null);

Expand Down Expand Up @@ -131,8 +137,15 @@ public void setStylingOptions(StylingOptions stylingOptions) {
applyStylingOptions();
}

public void setNightModeOption(int jsValue) {
super.setForceNightMode(EnumTranslationUtil.getForceNightModeFromJsValue(jsValue));
public void setNightModeOption(@ForceNightMode int nightModeOverride) {
this.nightModeOverride = nightModeOverride;
applyNightModePreference();
}

@Override
public void setMapColorScheme(@MapColorScheme int mapColorScheme) {
this.mapColorScheme = mapColorScheme;
applyMapColorSchemeToMap();
}

@Override
Expand Down Expand Up @@ -191,6 +204,16 @@ public GoogleMap getGoogleMap() {
return mGoogleMap;
}

private void applyMapColorSchemeToMap() {
if (mMapViewController != null) {
mMapViewController.setColorScheme(mapColorScheme);
}
}

private void applyNightModePreference() {
super.setForceNightMode(nightModeOverride);
}

private void cleanup() {
removeOnRecenterButtonClickedListener(onRecenterButtonClickedListener);
removePromptVisibilityChangedListener(onPromptVisibilityChangedListener);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public class NavViewManager extends SimpleViewManager<FrameLayout> {

private final HashMap<Integer, WeakReference<IMapViewFragment>> fragmentMap = new HashMap<>();

// Cache the latest options per view so deferred fragment creation uses fresh values.
private final HashMap<Integer, ReadableMap> mapOptionsCache = new HashMap<>();

private ReactApplicationContext reactContext;

public static synchronized NavViewManager getInstance(ReactApplicationContext reactContext) {
Expand All @@ -74,21 +77,27 @@ private boolean isFragmentCreated(int viewId) {

/** Builds GoogleMapOptions with all configured map settings. */
@NonNull
private GoogleMapOptions buildGoogleMapOptions(ReadableMap mapInitializationOptions) {
private GoogleMapOptions buildGoogleMapOptions(ReadableMap mapOptionsMap) {
GoogleMapOptions options = new GoogleMapOptions();
if (mapInitializationOptions == null) {
if (mapOptionsMap == null) {
return options;
}

if (mapInitializationOptions.hasKey("mapId") && !mapInitializationOptions.isNull("mapId")) {
String mapIdFromOptions = mapInitializationOptions.getString("mapId");
if (mapOptionsMap.hasKey("mapId") && !mapOptionsMap.isNull("mapId")) {
String mapIdFromOptions = mapOptionsMap.getString("mapId");
if (mapIdFromOptions != null && !mapIdFromOptions.isEmpty()) {
options.mapId(mapIdFromOptions);
}
}

if (mapInitializationOptions.hasKey("mapType") && !mapInitializationOptions.isNull("mapType")) {
options.mapType(mapInitializationOptions.getInt("mapType"));
if (mapOptionsMap.hasKey("mapType") && !mapOptionsMap.isNull("mapType")) {
options.mapType(mapOptionsMap.getInt("mapType"));
}

if (mapOptionsMap.hasKey("mapColorScheme")) {
int jsValue =
mapOptionsMap.isNull("mapColorScheme") ? 0 : mapOptionsMap.getInt("mapColorScheme");
options.mapColorScheme(EnumTranslationUtil.getMapColorSchemeFromJsValue(jsValue));
}

return options;
Expand Down Expand Up @@ -151,6 +160,7 @@ public void onDropViewInstance(@NonNull FrameLayout view) {
if (activity == null) return;

WeakReference<IMapViewFragment> weakReference = fragmentMap.remove(viewId);
mapOptionsCache.remove(viewId);
if (weakReference != null) {
IMapViewFragment fragment = weakReference.get();
if (fragment != null && fragment.isAdded()) {
Expand All @@ -163,17 +173,17 @@ public void onDropViewInstance(@NonNull FrameLayout view) {
}
}

@ReactProp(name = "mapInitializationOptions")
public void setMapInitializationOptions(
FrameLayout view, @NonNull ReadableMap mapInitializationOptions) {
@ReactProp(name = "mapOptions")
public void setMapOptions(FrameLayout view, @NonNull ReadableMap mapOptions) {
int viewId = view.getId();
mapOptionsCache.put(viewId, mapOptions);

// Don't create fragment if already exists
if (isFragmentCreated(viewId)) {
updateMapOptionValues(viewId, mapOptions);
return;
}

scheduleFragmentTransaction(view, mapInitializationOptions);
scheduleFragmentTransaction(view, mapOptions);
}

/** Map the "create" command to an integer */
Expand Down Expand Up @@ -317,7 +327,8 @@ public void receiveCommand(
navFragment = getNavFragmentForRoot(root);
if (navFragment != null) {
assert args != null;
navFragment.setNightModeOption(args.getInt(0));
int nightModeOverride = EnumTranslationUtil.getForceNightModeFromJsValue(args.getInt(0));
navFragment.setNightModeOption(nightModeOverride);
}
break;
case SET_SPEEDOMETER_ENABLED:
Expand Down Expand Up @@ -590,42 +601,81 @@ public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
}

private void scheduleFragmentTransaction(
@NonNull FrameLayout root, @NonNull ReadableMap mapInitializationOptions) {
@NonNull FrameLayout root, @NonNull ReadableMap mapOptions) {

// Commit the fragment transaction after view is added to the view hierarchy.
root.post(
() -> {
if (isFragmentCreated(root.getId())) {
int viewId = root.getId();
if (isFragmentCreated(viewId)) {
return;
}
commitFragmentTransaction(root, mapInitializationOptions);
ReadableMap latestOptions = mapOptionsCache.get(viewId);
ReadableMap optionsToUse = latestOptions != null ? latestOptions : mapOptions;
commitFragmentTransaction(root, optionsToUse);
});
}

private void updateMapOptionValues(int viewId, @NonNull ReadableMap mapOptions) {
IMapViewFragment fragment = getFragmentForViewId(viewId);
if (fragment == null) {
return;
}

if (mapOptions.hasKey("mapColorScheme")) {
int jsValue = mapOptions.isNull("mapColorScheme") ? 0 : mapOptions.getInt("mapColorScheme");
fragment.setMapColorScheme(EnumTranslationUtil.getMapColorSchemeFromJsValue(jsValue));
}

if (fragment instanceof INavViewFragment
&& mapOptions.hasKey("navigationStylingOptions")
&& !mapOptions.isNull("navigationStylingOptions")) {
ReadableMap stylingMap = mapOptions.getMap("navigationStylingOptions");
if (stylingMap != null) {
StylingOptions stylingOptions =
new StylingOptionsBuilder.Builder(stylingMap.toHashMap()).build();
((INavViewFragment) fragment).setStylingOptions(stylingOptions);
}
}

if (fragment instanceof INavViewFragment && mapOptions.hasKey("navigationNightMode")) {
int nightMode =
mapOptions.isNull("navigationNightMode") ? 0 : mapOptions.getInt("navigationNightMode");
((INavViewFragment) fragment)
.setNightModeOption(EnumTranslationUtil.getForceNightModeFromJsValue(nightMode));
}
}

/** Replace your React Native view with a custom fragment */
private void commitFragmentTransaction(
@NonNull FrameLayout view, @NonNull ReadableMap mapInitializationOptions) {
@NonNull FrameLayout view, @NonNull ReadableMap mapOptions) {

FragmentActivity activity = (FragmentActivity) reactContext.getCurrentActivity();
if (activity == null) return;
int viewId = view.getId();
Fragment fragment;

CustomTypes.MapViewType mapViewType =
EnumTranslationUtil.getMapViewTypeFromJsValue(
mapInitializationOptions.getInt("mapViewType"));
EnumTranslationUtil.getMapViewTypeFromJsValue(mapOptions.getInt("mapViewType"));

GoogleMapOptions googleMapOptions = buildGoogleMapOptions(mapInitializationOptions);
GoogleMapOptions googleMapOptions = buildGoogleMapOptions(mapOptions);

if (mapViewType == CustomTypes.MapViewType.MAP) {
fragment = MapViewFragment.newInstance(reactContext, viewId, googleMapOptions);
} else {
NavViewFragment navFragment =
NavViewFragment.newInstance(reactContext, viewId, googleMapOptions);
Integer nightMode = null;
if (mapOptions.hasKey("navigationNightMode")) {
int jsValue =
mapOptions.isNull("navigationNightMode") ? 0 : mapOptions.getInt("navigationNightMode");
nightMode = EnumTranslationUtil.getForceNightModeFromJsValue(jsValue);
navFragment.setNightModeOption(nightMode);
}

if (mapInitializationOptions.hasKey("navigationStylingOptions")
&& !mapInitializationOptions.isNull("navigationStylingOptions")) {
ReadableMap stylingOptionsMap = mapInitializationOptions.getMap("navigationStylingOptions");
if (mapOptions.hasKey("navigationStylingOptions")
&& !mapOptions.isNull("navigationStylingOptions")) {
ReadableMap stylingOptionsMap = mapOptions.getMap("navigationStylingOptions");
StylingOptions stylingOptions =
new StylingOptionsBuilder.Builder(stylingOptionsMap.toHashMap()).build();
navFragment.setStylingOptions(stylingOptions);
Expand Down
Loading
Loading