react-native-maps 1.21.0-alpha.58 → 1.21.0-alpha.59

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.
@@ -68,6 +68,8 @@ import com.google.maps.android.data.kml.KmlContainer;
68
68
  import com.google.maps.android.data.kml.KmlLayer;
69
69
  import com.google.maps.android.data.kml.KmlPlacemark;
70
70
 
71
+ import org.json.JSONException;
72
+ import org.json.JSONObject;
71
73
  import org.xmlpull.v1.XmlPullParserException;
72
74
 
73
75
  import java.io.IOException;
@@ -78,1505 +80,1566 @@ import java.util.HashMap;
78
80
  import java.util.List;
79
81
  import java.util.Map;
80
82
  import java.util.concurrent.ExecutionException;
83
+
81
84
  import com.rnmaps.fabric.event.*;
82
85
 
83
86
  public class MapView extends com.google.android.gms.maps.MapView implements GoogleMap.InfoWindowAdapter,
84
- GoogleMap.OnMarkerDragListener, OnMapReadyCallback, GoogleMap.OnPoiClickListener, GoogleMap.OnIndoorStateChangeListener {
85
- public GoogleMap map;
86
- private MarkerManager markerManager;
87
- private MarkerManager.Collection markerCollection;
88
- private PolylineManager polylineManager;
89
- private PolylineManager.Collection polylineCollection;
90
- private PolygonManager polygonManager;
91
- private PolygonManager.Collection polygonCollection;
92
- private CircleManager.Collection circleCollection;
93
- private GroundOverlayManager groundOverlayManager;
94
- private GroundOverlayManager.Collection groundOverlayCollection;
95
- private ProgressBar mapLoadingProgressBar;
96
- private RelativeLayout mapLoadingLayout;
97
- private ImageView cacheImageView;
98
- private Boolean isMapLoaded = false;
99
- private Integer loadingBackgroundColor = null;
100
- private Integer loadingIndicatorColor = null;
101
-
102
- private LatLngBounds boundsToMove;
103
- private CameraUpdate cameraToSet;
104
- private boolean setPaddingDeferred = false;
105
- private boolean showUserLocation = false;
106
- private boolean handlePanDrag = false;
107
- private boolean moveOnMarkerPress = true;
108
- private boolean cacheEnabled = false;
109
- private boolean poiClickEnabled = true;
110
-
111
- private ReadableMap initialRegion;
112
- private ReadableMap region;
113
- private ReadableMap initialCamera;
114
- private ReadableMap camera;
115
- private String customMapStyleString;
116
- private boolean initialRegionSet = false;
117
- private boolean initialCameraSet = false;
118
- private LatLngBounds cameraLastIdleBounds;
119
- private int cameraMoveReason = 0;
120
- private MapMarker selectedMarker;
121
-
122
- private static final String[] PERMISSIONS = new String[]{
123
- "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
124
-
125
- private final List<MapFeature> features = new ArrayList<>();
126
- private final Map<Marker, MapMarker> markerMap = new HashMap<>();
127
- private final Map<Polyline, MapPolyline> polylineMap = new HashMap<>();
128
- private final Map<Polygon, MapPolygon> polygonMap = new HashMap<>();
129
- private final Map<GroundOverlay, MapOverlay> overlayMap = new HashMap<>();
130
- private final Map<TileOverlay, MapHeatmap> heatmapMap = new HashMap<>();
131
- private final Map<TileOverlay, MapGradientPolyline> gradientPolylineMap = new HashMap<>();
132
- private final GestureDetectorCompat gestureDetector;
133
- private LifecycleEventListener lifecycleListener;
134
- private boolean paused = false;
135
- private boolean destroyed = false;
136
- private final ThemedReactContext context;
137
- private final FusedLocationSource fusedLocationSource;
138
-
139
- private final ViewAttacherGroup attacherGroup;
140
- private LatLng tapLocation;
141
- private Float maxZoomLevel;
142
- private Float minZoomLevel;
143
- private Boolean pitchEnabled;
144
- private Boolean showsCompass;
145
- private Boolean rotateEnabled;
146
- private Boolean zoomEnabled;
147
- private Boolean zoomControlEnabled;
148
- private Boolean setShowBuildings;
149
- private Boolean showsIndoorLevelPicker;
150
- private Boolean showIndoors;
151
- private Boolean scrollEnabled;
152
- private Boolean scrollDuringRotateOrZoomEnabled;
153
-
154
- private static boolean contextHasBug(Context context) {
155
- return context == null ||
156
- context.getResources() == null ||
157
- context.getResources().getConfiguration() == null;
158
- }
159
-
160
- // We do this to fix this bug:
161
- // https://github.com/react-native-maps/react-native-maps/issues/271
162
- //
163
- // which conflicts with another bug regarding the passed in context:
164
- // https://github.com/react-native-maps/react-native-maps/issues/1147
165
- //
166
- // Doing this allows us to avoid both bugs.
167
- private static Context getNonBuggyContext(ThemedReactContext reactContext,
168
- ReactApplicationContext appContext) {
169
- Context superContext = reactContext;
170
- if (!contextHasBug(appContext.getCurrentActivity())) {
171
- superContext = appContext.getCurrentActivity();
172
- } else if (contextHasBug(superContext)) {
173
- // we have the bug! let's try to find a better context to use
174
- if (!contextHasBug(reactContext.getCurrentActivity())) {
175
- superContext = reactContext.getCurrentActivity();
176
- } else if (!contextHasBug(reactContext.getApplicationContext())) {
177
- superContext = reactContext.getApplicationContext();
178
- }
179
-
180
- }
181
- return superContext;
182
- }
183
- public MapView(ThemedReactContext context,
184
- GoogleMapOptions googleMapOptions) {
185
- super(context, googleMapOptions);
186
- // todo add support for legacy renderer
187
- this.context = context;
188
- super.onCreate(null);
189
- super.onResume();
190
- super.getMapAsync(this);
191
-
192
- final MapView view = this;
193
-
194
- fusedLocationSource = new FusedLocationSource(context);
195
-
196
- gestureDetector =
197
- new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener() {
198
-
199
- @Override
200
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
201
- float distanceY) {
202
- if (handlePanDrag) {
203
- onPanDrag(e2);
87
+ GoogleMap.OnMarkerDragListener, OnMapReadyCallback, GoogleMap.OnPoiClickListener, GoogleMap.OnIndoorStateChangeListener {
88
+ public GoogleMap map;
89
+ private MarkerManager markerManager;
90
+ private MarkerManager.Collection markerCollection;
91
+ private PolylineManager polylineManager;
92
+ private PolylineManager.Collection polylineCollection;
93
+ private PolygonManager polygonManager;
94
+ private PolygonManager.Collection polygonCollection;
95
+ private CircleManager.Collection circleCollection;
96
+ private GroundOverlayManager groundOverlayManager;
97
+ private GroundOverlayManager.Collection groundOverlayCollection;
98
+ private ProgressBar mapLoadingProgressBar;
99
+ private RelativeLayout mapLoadingLayout;
100
+ private ImageView cacheImageView;
101
+ private Boolean isMapLoaded = false;
102
+ private Integer loadingBackgroundColor = null;
103
+ private Integer loadingIndicatorColor = null;
104
+
105
+ private LatLngBounds boundsToMove;
106
+ private CameraUpdate cameraToSet;
107
+ private boolean setPaddingDeferred = false;
108
+ private boolean showUserLocation = false;
109
+ private boolean handlePanDrag = false;
110
+ private boolean moveOnMarkerPress = true;
111
+ private boolean cacheEnabled = false;
112
+ private boolean poiClickEnabled = true;
113
+
114
+ private ReadableMap initialRegion;
115
+ private ReadableMap region;
116
+ private ReadableMap initialCamera;
117
+ private ReadableMap camera;
118
+ private String customMapStyleString;
119
+ private boolean initialRegionSet = false;
120
+ private boolean initialCameraSet = false;
121
+ private LatLngBounds cameraLastIdleBounds;
122
+ private int cameraMoveReason = 0;
123
+ private MapMarker selectedMarker;
124
+
125
+ private static final String[] PERMISSIONS = new String[]{
126
+ "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION"};
127
+
128
+ private final List<MapFeature> features = new ArrayList<>();
129
+ private final Map<Marker, MapMarker> markerMap = new HashMap<>();
130
+ private final Map<Polyline, MapPolyline> polylineMap = new HashMap<>();
131
+ private final Map<Polygon, MapPolygon> polygonMap = new HashMap<>();
132
+ private final Map<GroundOverlay, MapOverlay> overlayMap = new HashMap<>();
133
+ private final Map<TileOverlay, MapHeatmap> heatmapMap = new HashMap<>();
134
+ private final Map<TileOverlay, MapGradientPolyline> gradientPolylineMap = new HashMap<>();
135
+ private final GestureDetectorCompat gestureDetector;
136
+ private LifecycleEventListener lifecycleListener;
137
+ private boolean paused = false;
138
+ private boolean destroyed = false;
139
+ private final ThemedReactContext context;
140
+ private final FusedLocationSource fusedLocationSource;
141
+
142
+ private final ViewAttacherGroup attacherGroup;
143
+ private LatLng tapLocation;
144
+ private Float maxZoomLevel;
145
+ private Float minZoomLevel;
146
+ private Boolean pitchEnabled;
147
+ private Boolean showsCompass;
148
+ private Boolean rotateEnabled;
149
+ private Boolean zoomEnabled;
150
+ private Boolean zoomControlEnabled;
151
+ private Boolean setShowBuildings;
152
+ private Boolean showsIndoorLevelPicker;
153
+ private Boolean showIndoors;
154
+ private Boolean scrollEnabled;
155
+ private Boolean scrollDuringRotateOrZoomEnabled;
156
+
157
+ private static boolean contextHasBug(Context context) {
158
+ return context == null ||
159
+ context.getResources() == null ||
160
+ context.getResources().getConfiguration() == null;
161
+ }
162
+
163
+ // We do this to fix this bug:
164
+ // https://github.com/react-native-maps/react-native-maps/issues/271
165
+ //
166
+ // which conflicts with another bug regarding the passed in context:
167
+ // https://github.com/react-native-maps/react-native-maps/issues/1147
168
+ //
169
+ // Doing this allows us to avoid both bugs.
170
+ private static Context getNonBuggyContext(ThemedReactContext reactContext,
171
+ ReactApplicationContext appContext) {
172
+ Context superContext = reactContext;
173
+ if (!contextHasBug(appContext.getCurrentActivity())) {
174
+ superContext = appContext.getCurrentActivity();
175
+ } else if (contextHasBug(superContext)) {
176
+ // we have the bug! let's try to find a better context to use
177
+ if (!contextHasBug(reactContext.getCurrentActivity())) {
178
+ superContext = reactContext.getCurrentActivity();
179
+ } else if (!contextHasBug(reactContext.getApplicationContext())) {
180
+ superContext = reactContext.getApplicationContext();
181
+ }
182
+
183
+ }
184
+ return superContext;
185
+ }
186
+
187
+ public MapView(ThemedReactContext context,
188
+ GoogleMapOptions googleMapOptions) {
189
+ super(context, googleMapOptions);
190
+ // todo add support for legacy renderer
191
+ this.context = context;
192
+ super.onCreate(null);
193
+ super.onResume();
194
+ super.getMapAsync(this);
195
+
196
+ final MapView view = this;
197
+
198
+ fusedLocationSource = new FusedLocationSource(context);
199
+
200
+ gestureDetector =
201
+ new GestureDetectorCompat(context, new GestureDetector.SimpleOnGestureListener() {
202
+
203
+ @Override
204
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
205
+ float distanceY) {
206
+ if (handlePanDrag) {
207
+ onPanDrag(e2);
208
+ }
209
+ return false;
210
+ }
211
+
212
+ @Override
213
+ public boolean onDoubleTap(MotionEvent ev) {
214
+ onDoublePress(ev);
215
+ return false;
216
+ }
217
+ });
218
+
219
+ this.addOnLayoutChangeListener(new OnLayoutChangeListener() {
220
+ @Override
221
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
222
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
223
+ if (!paused) {
224
+ MapView.this.cacheView();
204
225
  }
205
- return false;
206
- }
207
-
208
- @Override
209
- public boolean onDoubleTap(MotionEvent ev) {
210
- onDoublePress(ev);
211
- return false;
212
- }
213
- });
214
-
215
- this.addOnLayoutChangeListener(new OnLayoutChangeListener() {
216
- @Override public void onLayoutChange(View v, int left, int top, int right, int bottom,
217
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
226
+ }
227
+ });
228
+
229
+
230
+ // Set up a parent view for triggering visibility in subviews that depend on it.
231
+ // Mainly ReactImageView depends on Fresco which depends on onVisibilityChanged() event
232
+ attacherGroup = new ViewAttacherGroup(context);
233
+ LayoutParams attacherLayoutParams = new LayoutParams(0, 0);
234
+ attacherLayoutParams.width = 0;
235
+ attacherLayoutParams.height = 0;
236
+ attacherLayoutParams.leftMargin = 99999999;
237
+ attacherLayoutParams.topMargin = 99999999;
238
+ attacherGroup.setLayoutParams(attacherLayoutParams);
239
+ addView(attacherGroup);
240
+ }
241
+
242
+ public MapView(ThemedReactContext reactContext, ReactApplicationContext appContext,
243
+ MapManager manager,
244
+ GoogleMapOptions googleMapOptions) {
245
+ this(null, googleMapOptions);
246
+
247
+ }
248
+
249
+
250
+ @FunctionalInterface
251
+ public interface EventCreator<T extends Event> {
252
+ T create(int surfaceId, int viewId, WritableMap payload);
253
+ }
254
+
255
+ private <T extends Event> void dispatchEvent(WritableMap payload, EventCreator<T> creator) {
256
+ // Cast context to ReactContext
257
+ ReactContext reactContext = context;
258
+
259
+ // Get the event dispatcher
260
+ EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
261
+
262
+ // If there is a dispatcher, create and dispatch the event
263
+ if (eventDispatcher != null) {
264
+ int surfaceId = UIManagerHelper.getSurfaceId(reactContext);
265
+ T event = creator.create(surfaceId, getId(), payload);
266
+ eventDispatcher.dispatchEvent(event);
267
+ }
268
+ }
269
+
270
+ @Override
271
+ public void onMapReady(@NonNull final GoogleMap map) {
272
+ if (destroyed) {
273
+ return;
274
+ }
275
+ this.map = map;
276
+ if (maxZoomLevel != null) {
277
+ setMaxZoomLevel(maxZoomLevel);
278
+ }
279
+ if (minZoomLevel != null) {
280
+ setMinZoomLevel(minZoomLevel);
281
+ }
282
+ if (pitchEnabled != null) {
283
+ setPitchEnabled(pitchEnabled);
284
+ }
285
+ if (showsCompass != null) {
286
+ setShowsCompass(showsCompass);
287
+ }
288
+ if (rotateEnabled != null) {
289
+ setRotateEnabled(rotateEnabled);
290
+ }
291
+ if (zoomEnabled != null) {
292
+ setZoomEnabled(zoomEnabled);
293
+ }
294
+ if (zoomControlEnabled != null) {
295
+ setZoomControlEnabled(zoomControlEnabled);
296
+ }
297
+ if (setShowBuildings != null) {
298
+ setShowBuildings(setShowBuildings);
299
+ }
300
+ if (showsIndoorLevelPicker != null) {
301
+ setShowsIndoorLevelPicker(showsIndoorLevelPicker);
302
+ }
303
+ if (showIndoors != null) {
304
+ setShowIndoors(showIndoors);
305
+ }
306
+ if (scrollEnabled != null) {
307
+ setScrollEnabled(scrollEnabled);
308
+ }
309
+ if (scrollDuringRotateOrZoomEnabled != null) {
310
+ setScrollDuringRotateOrZoomEnabled(scrollDuringRotateOrZoomEnabled);
311
+ }
312
+
313
+ markerManager = new MarkerManager(map);
314
+ markerCollection = markerManager.newCollection();
315
+ polylineManager = new PolylineManager(map);
316
+ polylineCollection = polylineManager.newCollection();
317
+ polygonManager = new PolygonManager(map);
318
+ polygonCollection = polygonManager.newCollection();
319
+ CircleManager circleManager = new CircleManager(map);
320
+ circleCollection = circleManager.newCollection();
321
+ groundOverlayManager = new GroundOverlayManager(map);
322
+ groundOverlayCollection = groundOverlayManager.newCollection();
323
+
324
+ markerCollection.setInfoWindowAdapter(this);
325
+ markerCollection.setOnMarkerDragListener(this);
326
+ this.map.setOnIndoorStateChangeListener(this);
327
+
328
+ applyBridgedProps();
329
+ dispatchEvent(new WritableNativeMap(), OnMapReadyEvent::new);
330
+
331
+ final MapView view = this;
332
+
333
+ map.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() {
334
+ @Override
335
+ public void onMyLocationChange(Location location) {
336
+ WritableMap event = new WritableNativeMap();
337
+
338
+ WritableMap coordinate = new WritableNativeMap();
339
+ coordinate.putDouble("latitude", location.getLatitude());
340
+ coordinate.putDouble("longitude", location.getLongitude());
341
+ coordinate.putDouble("altitude", location.getAltitude());
342
+ coordinate.putDouble("timestamp", location.getTime());
343
+ coordinate.putDouble("accuracy", location.getAccuracy());
344
+ coordinate.putDouble("speed", location.getSpeed());
345
+ coordinate.putDouble("heading", location.getBearing());
346
+ coordinate.putBoolean("isFromMockProvider", location.isFromMockProvider());
347
+
348
+ event.putMap("coordinate", coordinate);
349
+ dispatchEvent(event, OnUserLocationChangeEvent::new);
350
+ }
351
+ });
352
+
353
+ markerCollection.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
354
+ @Override
355
+ public boolean onMarkerClick(@NonNull Marker marker) {
356
+ MapMarker airMapMarker = getMarkerMap(marker);
357
+
358
+ WritableMap eventData = makeClickEventData(marker.getPosition());
359
+ eventData.putString("action", "marker-press");
360
+ eventData.putString("id", airMapMarker.getIdentifier());
361
+
362
+ dispatchEvent(eventData, OnMarkerPressEvent::new);
363
+
364
+
365
+ eventData = makeClickEventData(marker.getPosition());
366
+ eventData.putString("action", "marker-press");
367
+ eventData.putString("id", airMapMarker.getIdentifier());
368
+
369
+ dispatchEvent(eventData, OnPressEvent::new);
370
+
371
+
372
+ handleMarkerSelection(airMapMarker);
373
+
374
+ // Return false to open the callout info window and center on the marker
375
+ // https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap
376
+ // .OnMarkerClickListener
377
+ if (view.moveOnMarkerPress) {
378
+ return false;
379
+ } else {
380
+ marker.showInfoWindow();
381
+ return true;
382
+ }
383
+ }
384
+ });
385
+
386
+ polygonCollection.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
387
+ @Override
388
+ public void onPolygonClick(@NonNull Polygon polygon) {
389
+ WritableMap event = makeClickEventData(tapLocation);
390
+ event.putString("action", "polygon-press");
391
+ // todo: use Fabric events
392
+ // manager.pushEvent(context, polygonMap.get(polygon), "onPress", event);
393
+ }
394
+ });
395
+
396
+ polylineCollection.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() {
397
+ @Override
398
+ public void onPolylineClick(@NonNull Polyline polyline) {
399
+ WritableMap event = makeClickEventData(tapLocation);
400
+ event.putString("action", "polyline-press");
401
+ // todo: use Fabric events
402
+ // manager.pushEvent(context, polylineMap.get(polyline), "onPress", event);
403
+ }
404
+ });
405
+
406
+ markerCollection.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
407
+ @Override
408
+ public void onInfoWindowClick(@NonNull Marker marker) {
409
+ WritableMap event = makeClickEventData(marker.getPosition());
410
+ event.putString("action", "callout-press");
411
+ // todo: use Fabric events
412
+ // manager.pushEvent(context, view, "onCalloutPress", event);
413
+
414
+ event = makeClickEventData(marker.getPosition());
415
+ event.putString("action", "callout-press");
416
+ MapMarker markerView = getMarkerMap(marker);
417
+ // todo: use Fabric events
418
+ // manager.pushEvent(context, markerView, "onCalloutPress", event);
419
+
420
+ event = makeClickEventData(marker.getPosition());
421
+ event.putString("action", "callout-press");
422
+ MapCallout infoWindow = markerView.getCalloutView();
423
+ // todo: use Fabric events
424
+ // if (infoWindow != null) manager.pushEvent(context, infoWindow, "onPress", event);
425
+ }
426
+ });
427
+
428
+ map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
429
+ @Override
430
+ public void onMapClick(@NonNull LatLng point) {
431
+ WritableMap event = makeClickEventData(point);
432
+ event.putString("action", "press");
433
+ dispatchEvent(event, OnPressEvent::new);
434
+
435
+ handleMarkerSelection(null);
436
+ }
437
+ });
438
+
439
+ map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
440
+ @Override
441
+ public void onMapLongClick(@NonNull LatLng point) {
442
+ WritableMap event = makeClickEventData(point);
443
+ event.putString("action", "long-press");
444
+ dispatchEvent(event, OnLongPressEvent::new);
445
+ }
446
+ });
447
+
448
+ groundOverlayCollection.setOnGroundOverlayClickListener(new GoogleMap.OnGroundOverlayClickListener() {
449
+ @Override
450
+ public void onGroundOverlayClick(@NonNull GroundOverlay groundOverlay) {
451
+ WritableMap event = makeClickEventData(groundOverlay.getPosition());
452
+ event.putString("action", "overlay-press");
453
+ // todo: use Fabric events
454
+ // manager.pushEvent(context, overlayMap.get(groundOverlay), "onPress", event);
455
+ }
456
+ });
457
+
458
+ map.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
459
+ @Override
460
+ public void onCameraMoveStarted(int reason) {
461
+ cameraMoveReason = reason;
462
+ boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == reason;
463
+ WritableMap event = new WritableNativeMap();
464
+ event.putBoolean("isGesture", isGesture);
465
+ // todo: use Fabric events
466
+ // manager.pushEvent(context, view, "onRegionChangeStart", event);
467
+ }
468
+ });
469
+
470
+ map.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
471
+ @Override
472
+ public void onCameraMove() {
473
+ LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
474
+
475
+ cameraLastIdleBounds = null;
476
+ boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason;
477
+ WritableMap payload = OnRegionChangeEvent.payLoadFor(bounds, true, isGesture);
478
+ dispatchEvent(payload, OnRegionChangeEvent::new);
479
+ }
480
+ });
481
+
482
+ map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
483
+ @Override
484
+ public void onCameraIdle() {
485
+ LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
486
+ if ((cameraMoveReason != 0) &&
487
+ ((cameraLastIdleBounds == null) ||
488
+ LatLngBoundsUtils.BoundsAreDifferent(bounds, cameraLastIdleBounds))) {
489
+
490
+ cameraLastIdleBounds = bounds;
491
+ boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason;
492
+
493
+ RegionChangeEvent event = new RegionChangeEvent(getId(), bounds, false, isGesture);
494
+ // todo: use Fabric events
495
+ // eventDispatcher.dispatchEvent(event);
496
+ }
497
+ }
498
+ });
499
+
500
+ map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
501
+ @Override
502
+ public void onMapLoaded() {
503
+ isMapLoaded = true;
504
+ // todo: use Fabric events
505
+ // manager.pushEvent(context, view, "onMapLoaded", new WritableNativeMap());
506
+ MapView.this.cacheView();
507
+ }
508
+ });
509
+
510
+ // We need to be sure to disable location-tracking when app enters background, in-case some
511
+ // other module
512
+ // has acquired a wake-lock and is controlling location-updates, otherwise, location-manager
513
+ // will be left
514
+ // updating location constantly, killing the battery, even though some other location-mgmt
515
+ // module may
516
+ // desire to shut-down location-services.
517
+ lifecycleListener = new LifecycleEventListener() {
518
+ @Override
519
+ public void onHostResume() {
520
+ if (hasPermissions() && map != null) {
521
+ //noinspection MissingPermission
522
+ map.setMyLocationEnabled(showUserLocation);
523
+ map.setLocationSource(fusedLocationSource);
524
+ }
525
+ synchronized (MapView.this) {
526
+ if (!destroyed) {
527
+ MapView.this.onResume();
528
+ }
529
+ paused = false;
530
+ }
531
+ }
532
+
533
+ @Override
534
+ public void onHostPause() {
535
+ if (hasPermissions() && map != null) {
536
+ //noinspection MissingPermission
537
+ map.setMyLocationEnabled(false);
538
+ }
539
+ synchronized (MapView.this) {
540
+ if (!destroyed) {
541
+ MapView.this.onPause();
542
+ }
543
+ paused = true;
544
+ }
545
+ }
546
+
547
+ @Override
548
+ public void onHostDestroy() {
549
+ MapView.this.doDestroy();
550
+ }
551
+ };
552
+
553
+ context.addLifecycleEventListener(lifecycleListener);
554
+ }
555
+
556
+ private synchronized void handleMarkerSelection(MapMarker target) {
557
+ if (selectedMarker == target) {
558
+ return;
559
+ }
560
+
561
+ WritableMap event;
562
+
563
+ if (selectedMarker != null) {
564
+ event = makeClickEventData(selectedMarker.getPosition());
565
+ event.putString("action", "marker-deselect");
566
+ event.putString("id", selectedMarker.getIdentifier());
567
+ // todo: use Fabric events
568
+ // manager.pushEvent(context, selectedMarker, "onDeselect", event);
569
+
570
+ event = makeClickEventData(selectedMarker.getPosition());
571
+ event.putString("action", "marker-deselect");
572
+ event.putString("id", selectedMarker.getIdentifier());
573
+ dispatchEvent(event, OnMarkerDeselectEvent::new);
574
+ }
575
+
576
+ if (target != null) {
577
+ event = makeClickEventData(target.getPosition());
578
+ event.putString("action", "marker-select");
579
+ event.putString("id", target.getIdentifier());
580
+ // todo: use Fabric events
581
+ // manager.pushEvent(context, target, "onSelect", event);
582
+
583
+ event = makeClickEventData(target.getPosition());
584
+ event.putString("action", "marker-select");
585
+ event.putString("id", target.getIdentifier());
586
+ dispatchEvent(event, OnMarkerSelectEvent::new);
587
+ }
588
+
589
+ selectedMarker = target;
590
+ }
591
+
592
+ private boolean hasPermissions() {
593
+ return checkSelfPermission(getContext(), PERMISSIONS[0]) == PermissionChecker.PERMISSION_GRANTED ||
594
+ checkSelfPermission(getContext(), PERMISSIONS[1]) == PermissionChecker.PERMISSION_GRANTED;
595
+ }
596
+
597
+
598
+ public static Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
599
+ MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
600
+ builder.put(OnMarkerPressEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerPressEvent.EVENT_NAME));
601
+ builder.put(OnPressEvent.EVENT_NAME, MapBuilder.of("registrationName", OnPressEvent.EVENT_NAME));
602
+ builder.put(OnMarkerSelectEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerSelectEvent.EVENT_NAME));
603
+ builder.put(OnMarkerDeselectEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDeselectEvent.EVENT_NAME));
604
+ builder.put(OnMarkerDragEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDragEvent.EVENT_NAME));
605
+ builder.put(OnMarkerDragStartEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDragStartEvent.EVENT_NAME));
606
+ builder.put(OnMarkerDragEndEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDragEndEvent.EVENT_NAME));
607
+ builder.put(OnPoiClickEvent.EVENT_NAME, MapBuilder.of("registrationName", OnPoiClickEvent.EVENT_NAME));
608
+ builder.put(OnLongPressEvent.EVENT_NAME, MapBuilder.of("registrationName", OnLongPressEvent.EVENT_NAME));
609
+ return builder.build();
610
+ }
611
+
612
+ public static Map<String, Object> getExportedCustomDirectEventTypeConstants() {
613
+ return MapBuilder.of(
614
+ OnMapReadyEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMapReadyEvent.EVENT_NAME),
615
+ OnUserLocationChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", OnUserLocationChangeEvent.EVENT_NAME),
616
+ OnRegionChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", OnRegionChangeEvent.EVENT_NAME)
617
+ );
618
+ }
619
+
620
+
621
+ /*
622
+ onDestroy is final method so I can't override it.
623
+ */
624
+ public synchronized void doDestroy() {
625
+ if (destroyed) {
626
+ return;
627
+ }
628
+ destroyed = true;
629
+
630
+ if (lifecycleListener != null && context != null) {
631
+ context.removeLifecycleEventListener(lifecycleListener);
632
+ lifecycleListener = null;
633
+ }
218
634
  if (!paused) {
219
- MapView.this.cacheView();
635
+ onPause();
636
+ paused = true;
220
637
  }
221
- }
222
- });
638
+ onDestroy();
639
+ }
223
640
 
641
+ public void setInitialCameraSet(boolean initialCameraSet) {
642
+ this.initialCameraSet = initialCameraSet;
643
+ }
224
644
 
645
+ public void setInitialRegion(ReadableMap initialRegion) {
646
+ this.initialRegion = initialRegion;
647
+ // Theoretically onMapReady might be called before setInitialRegion
648
+ // In that case, trigger moveToRegion manually
649
+ if (!initialRegionSet && map != null) {
650
+ moveToRegion(initialRegion);
651
+ initialRegionSet = true;
652
+ }
653
+ }
225
654
 
226
- // Set up a parent view for triggering visibility in subviews that depend on it.
227
- // Mainly ReactImageView depends on Fresco which depends on onVisibilityChanged() event
228
- attacherGroup = new ViewAttacherGroup(context);
229
- LayoutParams attacherLayoutParams = new LayoutParams(0, 0);
230
- attacherLayoutParams.width = 0;
231
- attacherLayoutParams.height = 0;
232
- attacherLayoutParams.leftMargin = 99999999;
233
- attacherLayoutParams.topMargin = 99999999;
234
- attacherGroup.setLayoutParams(attacherLayoutParams);
235
- addView(attacherGroup);
236
- }
655
+ public void setInitialCamera(ReadableMap initialCamera) {
656
+ this.initialCamera = initialCamera;
657
+ if (!initialCameraSet && map != null) {
658
+ moveToCamera(initialCamera);
659
+ initialCameraSet = true;
660
+ }
661
+ }
237
662
 
238
- public MapView(ThemedReactContext reactContext, ReactApplicationContext appContext,
239
- MapManager manager,
240
- GoogleMapOptions googleMapOptions) {
241
- this(null, googleMapOptions);
663
+ private void applyBridgedProps() {
664
+ if (initialRegion != null) {
665
+ moveToRegion(initialRegion);
666
+ initialRegionSet = true;
667
+ } else if (region != null) {
668
+ moveToRegion(region);
669
+ } else if (initialCamera != null) {
670
+ moveToCamera(initialCamera);
671
+ initialCameraSet = true;
672
+ } else if (camera != null) {
673
+ moveToCamera(camera);
674
+ }
675
+ if (customMapStyleString != null) {
676
+ map.setMapStyle(new MapStyleOptions(customMapStyleString));
677
+ }
678
+ this.setPoiClickEnabled(poiClickEnabled);
679
+ }
680
+
681
+ public static LatLngBounds latLngBoundsFromRegion(ReadableMap region) {
682
+ if (region == null) return null;
683
+
684
+ double lng = region.getDouble("longitude");
685
+ double lat = region.getDouble("latitude");
686
+ double lngDelta = region.getDouble("longitudeDelta");
687
+ double latDelta = region.getDouble("latitudeDelta");
688
+ return new LatLngBounds(
689
+ new LatLng(lat - latDelta / 2, lng - lngDelta / 2), // southwest
690
+ new LatLng(lat + latDelta / 2, lng + lngDelta / 2) // northeast
691
+ );
692
+ }
693
+
694
+ private void moveToRegion(ReadableMap region) {
695
+ LatLngBounds bounds = latLngBoundsFromRegion(region);
696
+ if (bounds == null) return;
697
+ double lng = region.getDouble("longitude");
698
+ double lat = region.getDouble("latitude");
699
+ if (super.getHeight() <= 0 || super.getWidth() <= 0) {
700
+ // in this case, our map has not been laid out yet, so we save the bounds in a local
701
+ // variable, and make a guess of zoomLevel 10. Not to worry, though: as soon as layout
702
+ // occurs, we will move the camera to the saved bounds. Note that if we tried to move
703
+ // to the bounds now, it would trigger an exception.
704
+ map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lng), 10));
705
+ boundsToMove = bounds;
706
+ } else {
707
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
708
+ boundsToMove = null;
709
+ }
710
+ }
242
711
 
243
- }
712
+ public void setRegion(ReadableMap region) {
713
+ this.region = region;
714
+ if (region != null && map != null) {
715
+ moveToRegion(region);
716
+ }
717
+ }
244
718
 
719
+ public void setCamera(ReadableMap camera) {
720
+ this.camera = camera;
721
+ if (camera != null && map != null) {
722
+ moveToCamera(camera);
723
+ }
724
+ }
725
+
726
+ public static CameraPosition cameraPositionFromMap(ReadableMap camera) {
727
+ if (camera == null) return null;
245
728
 
246
- @FunctionalInterface
247
- public interface EventCreator<T extends Event> {
248
- T create(int surfaceId, int viewId, WritableMap payload);
249
- }
250
- private <T extends Event> void dispatchEvent(WritableMap payload, EventCreator<T> creator) {
251
- // Cast context to ReactContext
252
- ReactContext reactContext = context;
729
+ CameraPosition.Builder builder = new CameraPosition.Builder();
253
730
 
254
- // Get the event dispatcher
255
- EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, getId());
731
+ ReadableMap center = camera.getMap("center");
732
+ if (center != null) {
733
+ double lng = center.getDouble("longitude");
734
+ double lat = center.getDouble("latitude");
735
+ builder.target(new LatLng(lat, lng));
736
+ }
737
+
738
+ builder.tilt((float) camera.getDouble("pitch"));
739
+ builder.bearing((float) camera.getDouble("heading"));
740
+ builder.zoom((float) camera.getDouble("zoom"));
256
741
 
257
- // If there is a dispatcher, create and dispatch the event
258
- if (eventDispatcher != null) {
259
- int surfaceId = UIManagerHelper.getSurfaceId(reactContext);
260
- T event = creator.create(surfaceId, getId(), payload);
261
- eventDispatcher.dispatchEvent(event);
742
+ return builder.build();
262
743
  }
263
- }
264
744
 
265
- @Override
266
- public void onMapReady(@NonNull final GoogleMap map) {
267
- if (destroyed) {
268
- return;
745
+ public CameraPosition cameraPositionFromJSON(JSONObject camera) throws JSONException {
746
+ if (camera == null) return null;
747
+ if (map == null) return null;
748
+
749
+ CameraPosition.Builder builder = new CameraPosition.Builder();
750
+
751
+ if (camera.has("center")) {
752
+ JSONObject center = camera.getJSONObject("center");
753
+ if (center != null) {
754
+ double lng = center.getDouble("longitude");
755
+ double lat = center.getDouble("latitude");
756
+ builder.target(new LatLng(lat, lng));
757
+ }
758
+ } else {
759
+ builder.target(map.getCameraPosition().target);
760
+ }
761
+
762
+ if (camera.has("pitch")) {
763
+ builder.tilt((float) camera.getDouble("pitch"));
764
+ } else {
765
+ builder.tilt(map.getCameraPosition().tilt);
766
+ }
767
+ if (camera.has("heading")) {
768
+ builder.bearing((float) camera.getDouble("heading"));
769
+ } else {
770
+ builder.bearing(map.getCameraPosition().bearing);
771
+ }
772
+ if (camera.has("zoom")) {
773
+ builder.zoom((float) camera.getDouble("zoom"));
774
+ } else {
775
+ builder.zoom(map.getCameraPosition().zoom);
776
+ }
777
+
778
+ return builder.build();
269
779
  }
270
- this.map = map;
271
- if (maxZoomLevel != null){
272
- setMaxZoomLevel(maxZoomLevel);
780
+
781
+ public void moveToCamera(ReadableMap cameraMap) {
782
+ moveToCamera(cameraPositionFromMap(cameraMap));
273
783
  }
274
- if (minZoomLevel != null){
275
- setMinZoomLevel(minZoomLevel);
784
+
785
+ public void moveToCamera(CameraPosition camera) {
786
+ if (camera == null) return;
787
+ CameraUpdate update = CameraUpdateFactory.newCameraPosition(camera);
788
+
789
+ if (super.getHeight() <= 0 || super.getWidth() <= 0) {
790
+ // in this case, our map has not been laid out yet, so we save the camera update in a
791
+ // local variable. As soon as layout occurs, we will move the camera to the saved update.
792
+ // Note that if we tried to move to the camera now, it would trigger an exception.
793
+ cameraToSet = update;
794
+ } else {
795
+ map.moveCamera(update);
796
+ cameraToSet = null;
797
+ }
276
798
  }
277
- if (pitchEnabled != null){
278
- setPitchEnabled(pitchEnabled);
799
+
800
+ public void setMapStyle(@Nullable String customMapStyleString) {
801
+ this.customMapStyleString = customMapStyleString;
802
+ if (map != null && customMapStyleString != null) {
803
+ map.setMapStyle(new MapStyleOptions(customMapStyleString));
804
+ }
279
805
  }
280
- if (showsCompass != null){
281
- setShowsCompass(showsCompass);
806
+
807
+ public void setShowsUserLocation(boolean showUserLocation) {
808
+ this.showUserLocation = showUserLocation; // hold onto this for lifecycle handling
809
+ if (hasPermissions()) {
810
+ map.setLocationSource(fusedLocationSource);
811
+ //noinspection MissingPermission
812
+ map.setMyLocationEnabled(showUserLocation);
813
+ }
282
814
  }
283
- if (rotateEnabled != null){
284
- setRotateEnabled(rotateEnabled);
815
+
816
+ public void setUserLocationPriority(int priority) {
817
+ fusedLocationSource.setPriority(priority);
285
818
  }
286
- if (zoomEnabled != null){
287
- setZoomEnabled(zoomEnabled);
819
+
820
+ public void setUserLocationUpdateInterval(int interval) {
821
+ fusedLocationSource.setInterval(interval);
288
822
  }
289
- if (zoomControlEnabled != null){
290
- setZoomControlEnabled(zoomControlEnabled);
823
+
824
+ public void setUserLocationFastestInterval(int interval) {
825
+ fusedLocationSource.setFastestInterval(interval);
291
826
  }
292
- if (setShowBuildings != null){
293
- setShowBuildings(setShowBuildings);
827
+
828
+ public void setShowsMyLocationButton(boolean showMyLocationButton) {
829
+ if (map != null) {
830
+ if (hasPermissions() || !showMyLocationButton) {
831
+ map.getUiSettings().setMyLocationButtonEnabled(showMyLocationButton);
832
+ }
833
+ }
294
834
  }
295
- if (showsIndoorLevelPicker != null){
296
- setShowsIndoorLevelPicker(showsIndoorLevelPicker);
835
+
836
+ public void setToolbarEnabled(boolean toolbarEnabled) {
837
+ if (map != null) {
838
+ if (hasPermissions() || !toolbarEnabled) {
839
+ map.getUiSettings().setMapToolbarEnabled(toolbarEnabled);
840
+ }
841
+ }
297
842
  }
298
- if (showIndoors != null){
299
- setShowIndoors(showIndoors);
843
+
844
+ public void setMapType(int mapType) {
845
+ if (map != null) {
846
+ map.setMapType(mapType);
847
+ }
300
848
  }
301
- if (scrollEnabled != null){
302
- setScrollEnabled(scrollEnabled);
849
+
850
+ public void setCacheEnabled(boolean cacheEnabled) {
851
+ this.cacheEnabled = cacheEnabled;
852
+ this.cacheView();
303
853
  }
304
- if (scrollDuringRotateOrZoomEnabled != null){
305
- setScrollDuringRotateOrZoomEnabled(scrollDuringRotateOrZoomEnabled);
854
+
855
+ public void setPoiClickEnabled(boolean poiClickEnabled) {
856
+ this.poiClickEnabled = poiClickEnabled;
857
+ if (map != null) {
858
+ map.setOnPoiClickListener(poiClickEnabled ? this : null);
859
+ }
306
860
  }
307
861
 
308
- markerManager = new MarkerManager(map);
309
- markerCollection = markerManager.newCollection();
310
- polylineManager = new PolylineManager(map);
311
- polylineCollection = polylineManager.newCollection();
312
- polygonManager = new PolygonManager(map);
313
- polygonCollection = polygonManager.newCollection();
314
- CircleManager circleManager = new CircleManager(map);
315
- circleCollection = circleManager.newCollection();
316
- groundOverlayManager = new GroundOverlayManager(map);
317
- groundOverlayCollection = groundOverlayManager.newCollection();
862
+ public void setLoadingEnabled(boolean loadingEnabled) {
863
+ if (loadingEnabled && !this.isMapLoaded) {
864
+ this.getMapLoadingLayoutView().setVisibility(View.VISIBLE);
865
+ }
866
+ }
318
867
 
319
- markerCollection.setInfoWindowAdapter(this);
320
- markerCollection.setOnMarkerDragListener(this);
321
- this.map.setOnIndoorStateChangeListener(this);
868
+ public void setMoveOnMarkerPress(boolean moveOnPress) {
869
+ this.moveOnMarkerPress = moveOnPress;
870
+ }
322
871
 
323
- applyBridgedProps();
324
- dispatchEvent(new WritableNativeMap(), OnMapReadyEvent::new);
872
+ public void setMaxZoomLevel(float maxZoomLevel) {
873
+ this.maxZoomLevel = maxZoomLevel;
874
+ if (map != null) {
875
+ map.setMaxZoomPreference(maxZoomLevel);
876
+ }
877
+ }
325
878
 
326
- final MapView view = this;
879
+ public void setMinZoomLevel(float minZoomLevel) {
880
+ this.minZoomLevel = minZoomLevel;
881
+ if (map != null) {
882
+ map.setMinZoomPreference(minZoomLevel);
883
+ }
884
+ }
327
885
 
328
- map.setOnMyLocationChangeListener(new GoogleMap.OnMyLocationChangeListener() {
329
- @Override
330
- public void onMyLocationChange(Location location){
331
- WritableMap event = new WritableNativeMap();
886
+ public void setPitchEnabled(boolean pitchEnabled) {
887
+ this.pitchEnabled = pitchEnabled;
888
+ if (map != null) {
889
+ map.getUiSettings().setTiltGesturesEnabled(pitchEnabled);
890
+ }
891
+ }
332
892
 
333
- WritableMap coordinate = new WritableNativeMap();
334
- coordinate.putDouble("latitude", location.getLatitude());
335
- coordinate.putDouble("longitude", location.getLongitude());
336
- coordinate.putDouble("altitude", location.getAltitude());
337
- coordinate.putDouble("timestamp", location.getTime());
338
- coordinate.putDouble("accuracy", location.getAccuracy());
339
- coordinate.putDouble("speed", location.getSpeed());
340
- coordinate.putDouble("heading", location.getBearing());
341
- coordinate.putBoolean("isFromMockProvider", location.isFromMockProvider());
893
+ public void setShowsCompass(boolean showsCompass) {
894
+ this.showsCompass = showsCompass;
895
+ if (map != null) {
896
+ map.getUiSettings().setCompassEnabled(showsCompass);
897
+ }
898
+ }
342
899
 
343
- event.putMap("coordinate", coordinate);
344
- dispatchEvent(event, OnUserLocationChangeEvent::new);
345
- }
346
- });
900
+ public void setRotateEnabled(boolean rotateEnabled) {
901
+ this.rotateEnabled = rotateEnabled;
902
+ if (map != null) {
903
+ map.getUiSettings().setRotateGesturesEnabled(rotateEnabled);
904
+ }
905
+ }
347
906
 
348
- markerCollection.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
349
- @Override
350
- public boolean onMarkerClick(@NonNull Marker marker) {
351
- MapMarker airMapMarker = getMarkerMap(marker);
907
+ public void setZoomEnabled(boolean zoomEnabled) {
908
+ this.zoomEnabled = zoomEnabled;
909
+ if (map != null) {
910
+ map.getUiSettings().setZoomGesturesEnabled(zoomEnabled);
911
+ }
912
+ }
352
913
 
353
- WritableMap eventData = makeClickEventData(marker.getPosition());
354
- eventData.putString("action", "marker-press");
355
- eventData.putString("id", airMapMarker.getIdentifier());
914
+ public void setZoomControlEnabled(boolean zoomControlEnabled) {
915
+ this.zoomControlEnabled = zoomControlEnabled;
916
+ if (map != null) {
917
+ map.getUiSettings().setZoomControlsEnabled(zoomControlEnabled);
918
+ }
919
+ }
356
920
 
357
- dispatchEvent(eventData, OnMarkerPressEvent::new);
921
+ public void setScrollDuringRotateOrZoomEnabled(boolean scrollDuringRotateOrZoomEnabled) {
922
+ this.scrollDuringRotateOrZoomEnabled = scrollDuringRotateOrZoomEnabled;
923
+ if (map != null) {
924
+ map.getUiSettings().setScrollGesturesEnabledDuringRotateOrZoom(scrollDuringRotateOrZoomEnabled);
925
+ }
926
+ }
358
927
 
928
+ public void setScrollEnabled(boolean scrollEnabled) {
929
+ this.scrollEnabled = scrollEnabled;
930
+ if (map != null) {
931
+ map.getUiSettings().setScrollGesturesEnabled(scrollEnabled);
932
+ }
933
+ }
359
934
 
360
- eventData = makeClickEventData(marker.getPosition());
361
- eventData.putString("action", "marker-press");
362
- eventData.putString("id", airMapMarker.getIdentifier());
935
+ public void setShowIndoors(boolean showIndoors) {
936
+ this.showIndoors = showIndoors;
937
+ if (map != null) {
938
+ map.setIndoorEnabled(showIndoors);
939
+ }
940
+ }
363
941
 
364
- dispatchEvent(eventData, OnPressEvent::new);
942
+ public void setShowsIndoorLevelPicker(boolean showsIndoorLevelPicker) {
943
+ this.showsIndoorLevelPicker = showsIndoorLevelPicker;
944
+ if (map != null) {
945
+ map.getUiSettings().setIndoorLevelPickerEnabled(showsIndoorLevelPicker);
946
+ }
947
+ }
948
+
949
+ public void setShowBuildings(boolean showBuildings) {
950
+ this.setShowBuildings = showBuildings;
951
+ if (map != null) {
952
+ map.setBuildingsEnabled(showBuildings);
953
+ }
954
+ }
365
955
 
366
956
 
367
- handleMarkerSelection(airMapMarker);
957
+ public void setLoadingBackgroundColor(Integer loadingBackgroundColor) {
958
+ this.loadingBackgroundColor = loadingBackgroundColor;
368
959
 
369
- // Return false to open the callout info window and center on the marker
370
- // https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap
371
- // .OnMarkerClickListener
372
- if (view.moveOnMarkerPress) {
373
- return false;
960
+ if (this.mapLoadingLayout != null) {
961
+ if (loadingBackgroundColor == null) {
962
+ this.mapLoadingLayout.setBackgroundColor(Color.WHITE);
963
+ } else {
964
+ this.mapLoadingLayout.setBackgroundColor(this.loadingBackgroundColor);
965
+ }
966
+ }
967
+ }
968
+
969
+ public void setLoadingIndicatorColor(Integer loadingIndicatorColor) {
970
+ this.loadingIndicatorColor = loadingIndicatorColor;
971
+ if (this.mapLoadingProgressBar != null) {
972
+ Integer color = loadingIndicatorColor;
973
+ if (color == null) {
974
+ color = Color.parseColor("#606060");
975
+ }
976
+
977
+ ColorStateList progressTintList = ColorStateList.valueOf(loadingIndicatorColor);
978
+ ColorStateList secondaryProgressTintList = ColorStateList.valueOf(loadingIndicatorColor);
979
+ ColorStateList indeterminateTintList = ColorStateList.valueOf(loadingIndicatorColor);
980
+
981
+ this.mapLoadingProgressBar.setProgressTintList(progressTintList);
982
+ this.mapLoadingProgressBar.setSecondaryProgressTintList(secondaryProgressTintList);
983
+ this.mapLoadingProgressBar.setIndeterminateTintList(indeterminateTintList);
984
+ }
985
+ }
986
+
987
+ public void setHandlePanDrag(boolean handlePanDrag) {
988
+ this.handlePanDrag = handlePanDrag;
989
+ }
990
+
991
+ public void addFeature(View child, int index) {
992
+ // Our desired API is to pass up annotations/overlays as children to the mapview component.
993
+ // This is where we intercept them and do the appropriate underlying mapview action.
994
+ if (child instanceof MapMarker) {
995
+ MapMarker annotation = (MapMarker) child;
996
+ annotation.addToMap(markerCollection);
997
+ features.add(index, annotation);
998
+
999
+ // Allow visibility event to be triggered later
1000
+ int visibility = annotation.getVisibility();
1001
+ annotation.setVisibility(INVISIBLE);
1002
+
1003
+ // Remove from a view group if already present, prevent "specified child
1004
+ // already had a parent" error.
1005
+ ViewGroup annotationParent = (ViewGroup) annotation.getParent();
1006
+ if (annotationParent != null) {
1007
+ annotationParent.removeView(annotation);
1008
+ }
1009
+
1010
+ // Add to the parent group
1011
+ attacherGroup.addView(annotation);
1012
+
1013
+ // Trigger visibility event if necessary.
1014
+ // With some testing, seems like it is not always
1015
+ // triggered just by being added to a parent view.
1016
+ annotation.setVisibility(visibility);
1017
+
1018
+ Marker marker = (Marker) annotation.getFeature();
1019
+ markerMap.put(marker, annotation);
1020
+ } else if (child instanceof MapPolyline) {
1021
+ MapPolyline polylineView = (MapPolyline) child;
1022
+ polylineView.addToMap(polylineCollection);
1023
+ features.add(index, polylineView);
1024
+ Polyline polyline = (Polyline) polylineView.getFeature();
1025
+ polylineMap.put(polyline, polylineView);
1026
+ } else if (child instanceof MapGradientPolyline) {
1027
+ MapGradientPolyline polylineView = (MapGradientPolyline) child;
1028
+ polylineView.addToMap(map);
1029
+ features.add(index, polylineView);
1030
+ TileOverlay tileOverlay = (TileOverlay) polylineView.getFeature();
1031
+ gradientPolylineMap.put(tileOverlay, polylineView);
1032
+ } else if (child instanceof MapPolygon) {
1033
+ MapPolygon polygonView = (MapPolygon) child;
1034
+ polygonView.addToMap(polygonCollection);
1035
+ features.add(index, polygonView);
1036
+ Polygon polygon = (Polygon) polygonView.getFeature();
1037
+ polygonMap.put(polygon, polygonView);
1038
+ } else if (child instanceof MapCircle) {
1039
+ MapCircle circleView = (MapCircle) child;
1040
+ circleView.addToMap(circleCollection);
1041
+ features.add(index, circleView);
1042
+ } else if (child instanceof MapUrlTile) {
1043
+ MapUrlTile urlTileView = (MapUrlTile) child;
1044
+ urlTileView.addToMap(map);
1045
+ features.add(index, urlTileView);
1046
+ } else if (child instanceof MapWMSTile) {
1047
+ MapWMSTile urlTileView = (MapWMSTile) child;
1048
+ urlTileView.addToMap(map);
1049
+ features.add(index, urlTileView);
1050
+ } else if (child instanceof MapLocalTile) {
1051
+ MapLocalTile localTileView = (MapLocalTile) child;
1052
+ localTileView.addToMap(map);
1053
+ features.add(index, localTileView);
1054
+ } else if (child instanceof MapOverlay) {
1055
+ MapOverlay overlayView = (MapOverlay) child;
1056
+ overlayView.addToMap(groundOverlayCollection);
1057
+ features.add(index, overlayView);
1058
+ GroundOverlay overlay = (GroundOverlay) overlayView.getFeature();
1059
+ overlayMap.put(overlay, overlayView);
1060
+ } else if (child instanceof MapHeatmap) {
1061
+ MapHeatmap heatmapView = (MapHeatmap) child;
1062
+ heatmapView.addToMap(map);
1063
+ features.add(index, heatmapView);
1064
+ TileOverlay heatmap = (TileOverlay) heatmapView.getFeature();
1065
+ heatmapMap.put(heatmap, heatmapView);
1066
+ } else if (child instanceof ViewGroup) {
1067
+ ViewGroup children = (ViewGroup) child;
1068
+ for (int i = 0; i < children.getChildCount(); i++) {
1069
+ addFeature(children.getChildAt(i), index);
1070
+ }
374
1071
  } else {
375
- marker.showInfoWindow();
376
- return true;
1072
+ addView(child, index);
377
1073
  }
378
- }
379
- });
1074
+ }
380
1075
 
381
- polygonCollection.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() {
382
- @Override
383
- public void onPolygonClick(@NonNull Polygon polygon) {
384
- WritableMap event = makeClickEventData(tapLocation);
385
- event.putString("action", "polygon-press");
386
- // todo: use Fabric events
387
- // manager.pushEvent(context, polygonMap.get(polygon), "onPress", event);
388
- }
389
- });
390
-
391
- polylineCollection.setOnPolylineClickListener(new GoogleMap.OnPolylineClickListener() {
392
- @Override
393
- public void onPolylineClick(@NonNull Polyline polyline) {
394
- WritableMap event = makeClickEventData(tapLocation);
395
- event.putString("action", "polyline-press");
396
- // todo: use Fabric events
397
- // manager.pushEvent(context, polylineMap.get(polyline), "onPress", event);
398
- }
399
- });
1076
+ public int getFeatureCount() {
1077
+ return features.size();
1078
+ }
400
1079
 
401
- markerCollection.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {
402
- @Override
403
- public void onInfoWindowClick(@NonNull Marker marker) {
404
- WritableMap event = makeClickEventData(marker.getPosition());
405
- event.putString("action", "callout-press");
406
- // todo: use Fabric events
407
- // manager.pushEvent(context, view, "onCalloutPress", event);
1080
+ public View getFeatureAt(int index) {
1081
+ return features.get(index);
1082
+ }
408
1083
 
409
- event = makeClickEventData(marker.getPosition());
410
- event.putString("action", "callout-press");
411
- MapMarker markerView = getMarkerMap(marker);
412
- // todo: use Fabric events
413
- // manager.pushEvent(context, markerView, "onCalloutPress", event);
1084
+ public void removeFeatureAt(int index) {
1085
+ MapFeature feature = features.remove(index);
1086
+ if (feature instanceof MapMarker) {
1087
+ markerMap.remove(feature.getFeature());
1088
+ feature.removeFromMap(markerCollection);
1089
+ attacherGroup.removeView(feature);
1090
+ } else if (feature instanceof MapHeatmap) {
1091
+ heatmapMap.remove(feature.getFeature());
1092
+ feature.removeFromMap(map);
1093
+ } else if (feature instanceof MapCircle) {
1094
+ feature.removeFromMap(circleCollection);
1095
+ } else if (feature instanceof MapOverlay) {
1096
+ feature.removeFromMap(groundOverlayCollection);
1097
+ } else if (feature instanceof MapPolygon) {
1098
+ feature.removeFromMap(polygonCollection);
1099
+ } else if (feature instanceof MapPolyline) {
1100
+ feature.removeFromMap(polylineCollection);
1101
+ } else {
1102
+ feature.removeFromMap(map);
1103
+ }
1104
+ }
414
1105
 
415
- event = makeClickEventData(marker.getPosition());
416
- event.putString("action", "callout-press");
417
- MapCallout infoWindow = markerView.getCalloutView();
418
- // todo: use Fabric events
419
- // if (infoWindow != null) manager.pushEvent(context, infoWindow, "onPress", event);
420
- }
421
- });
422
-
423
- map.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
424
- @Override
425
- public void onMapClick(@NonNull LatLng point) {
426
- WritableMap event = makeClickEventData(point);
427
- event.putString("action", "press");
428
- dispatchEvent(event, OnPressEvent::new);
429
-
430
- handleMarkerSelection(null);
431
- }
432
- });
433
-
434
- map.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() {
435
- @Override
436
- public void onMapLongClick(@NonNull LatLng point) {
437
- WritableMap event = makeClickEventData(point);
438
- event.putString("action", "long-press");
439
- dispatchEvent(event, OnLongPressEvent::new);
440
- }
441
- });
442
-
443
- groundOverlayCollection.setOnGroundOverlayClickListener(new GoogleMap.OnGroundOverlayClickListener() {
444
- @Override
445
- public void onGroundOverlayClick(@NonNull GroundOverlay groundOverlay) {
446
- WritableMap event = makeClickEventData(groundOverlay.getPosition());
447
- event.putString("action", "overlay-press");
448
- // todo: use Fabric events
449
- // manager.pushEvent(context, overlayMap.get(groundOverlay), "onPress", event);
450
- }
451
- });
452
-
453
- map.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {
454
- @Override
455
- public void onCameraMoveStarted(int reason) {
456
- cameraMoveReason = reason;
457
- boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == reason;
1106
+ public WritableMap makeClickEventData(LatLng point) {
458
1107
  WritableMap event = new WritableNativeMap();
459
- event.putBoolean("isGesture", isGesture);
460
- // todo: use Fabric events
461
- // manager.pushEvent(context, view, "onRegionChangeStart", event);
462
- }
463
- });
464
1108
 
465
- map.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
466
- @Override
467
- public void onCameraMove() {
468
- LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
1109
+ WritableMap coordinate = new WritableNativeMap();
1110
+ coordinate.putDouble("latitude", point.latitude);
1111
+ coordinate.putDouble("longitude", point.longitude);
1112
+ event.putMap("coordinate", coordinate);
469
1113
 
470
- cameraLastIdleBounds = null;
471
- boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason;
472
- WritableMap payload = OnRegionChangeEvent.payLoadFor(bounds, true, isGesture);
473
- dispatchEvent(payload, OnRegionChangeEvent::new);
474
- }
475
- });
1114
+ Projection projection = map.getProjection();
1115
+ Point screenPoint = projection.toScreenLocation(point);
476
1116
 
477
- map.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
478
- @Override
479
- public void onCameraIdle() {
480
- LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
481
- if ((cameraMoveReason != 0) &&
482
- ((cameraLastIdleBounds == null) ||
483
- LatLngBoundsUtils.BoundsAreDifferent(bounds, cameraLastIdleBounds))) {
1117
+ WritableMap position = new WritableNativeMap();
1118
+ position.putDouble("x", screenPoint.x);
1119
+ position.putDouble("y", screenPoint.y);
1120
+ event.putMap("position", position);
484
1121
 
485
- cameraLastIdleBounds = bounds;
486
- boolean isGesture = GoogleMap.OnCameraMoveStartedListener.REASON_GESTURE == cameraMoveReason;
1122
+ return event;
1123
+ }
487
1124
 
488
- RegionChangeEvent event = new RegionChangeEvent(getId(), bounds, false, isGesture);
489
- // todo: use Fabric events
490
- // eventDispatcher.dispatchEvent(event);
491
- }
492
- }
493
- });
1125
+ public void updateExtraData(Object extraData) {
1126
+ if (setPaddingDeferred && super.getHeight() > 0 && super.getWidth() > 0) {
1127
+ CameraUpdate cu = CameraUpdateFactory.newCameraPosition(map.getCameraPosition());
494
1128
 
495
- map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
496
- @Override public void onMapLoaded() {
497
- isMapLoaded = true;
498
- // todo: use Fabric events
499
- // manager.pushEvent(context, view, "onMapLoaded", new WritableNativeMap());
500
- MapView.this.cacheView();
501
- }
502
- });
503
-
504
- // We need to be sure to disable location-tracking when app enters background, in-case some
505
- // other module
506
- // has acquired a wake-lock and is controlling location-updates, otherwise, location-manager
507
- // will be left
508
- // updating location constantly, killing the battery, even though some other location-mgmt
509
- // module may
510
- // desire to shut-down location-services.
511
- lifecycleListener = new LifecycleEventListener() {
512
- @Override
513
- public void onHostResume() {
514
- if (hasPermissions() && map != null) {
515
- //noinspection MissingPermission
516
- map.setMyLocationEnabled(showUserLocation);
517
- map.setLocationSource(fusedLocationSource);
518
- }
519
- synchronized (MapView.this) {
520
- if (!destroyed) {
521
- MapView.this.onResume();
522
- }
523
- paused = false;
524
- }
525
- }
526
-
527
- @Override
528
- public void onHostPause() {
529
- if (hasPermissions() && map != null) {
530
- //noinspection MissingPermission
531
- map.setMyLocationEnabled(false);
532
- }
533
- synchronized (MapView.this) {
534
- if (!destroyed) {
535
- MapView.this.onPause();
536
- }
537
- paused = true;
538
- }
539
- }
540
-
541
- @Override
542
- public void onHostDestroy() {
543
- MapView.this.doDestroy();
544
- }
545
- };
1129
+ map.setPadding(edgeLeftPadding + baseLeftMapPadding,
1130
+ edgeTopPadding + baseTopMapPadding,
1131
+ edgeRightPadding + baseRightMapPadding,
1132
+ edgeBottomPadding + baseBottomMapPadding);
1133
+ map.moveCamera(cu);
546
1134
 
547
- context.addLifecycleEventListener(lifecycleListener);
548
- }
549
-
550
- private synchronized void handleMarkerSelection(MapMarker target) {
551
- if (selectedMarker == target) {
552
- return;
553
- }
554
-
555
- WritableMap event;
1135
+ // Move the google logo to the default base padding value.
1136
+ map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
556
1137
 
557
- if (selectedMarker != null) {
558
- event = makeClickEventData(selectedMarker.getPosition());
559
- event.putString("action", "marker-deselect");
560
- event.putString("id", selectedMarker.getIdentifier());
561
- // todo: use Fabric events
562
- // manager.pushEvent(context, selectedMarker, "onDeselect", event);
563
-
564
- event = makeClickEventData(selectedMarker.getPosition());
565
- event.putString("action", "marker-deselect");
566
- event.putString("id", selectedMarker.getIdentifier());
567
- dispatchEvent(event, OnMarkerDeselectEvent::new);
568
- }
569
-
570
- if (target != null) {
571
- event = makeClickEventData(target.getPosition());
572
- event.putString("action", "marker-select");
573
- event.putString("id", target.getIdentifier());
574
- // todo: use Fabric events
575
- // manager.pushEvent(context, target, "onSelect", event);
576
-
577
- event = makeClickEventData(target.getPosition());
578
- event.putString("action", "marker-select");
579
- event.putString("id", target.getIdentifier());
580
- dispatchEvent(event, OnMarkerSelectEvent::new);
581
- }
582
-
583
- selectedMarker = target;
584
- }
585
-
586
- private boolean hasPermissions() {
587
- return checkSelfPermission(getContext(), PERMISSIONS[0]) == PermissionChecker.PERMISSION_GRANTED ||
588
- checkSelfPermission(getContext(), PERMISSIONS[1]) == PermissionChecker.PERMISSION_GRANTED;
589
- }
590
-
591
-
592
- public static Map<String, Object> getExportedCustomBubblingEventTypeConstants() {
593
- MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
594
- builder.put(OnMarkerPressEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerPressEvent.EVENT_NAME));
595
- builder.put(OnPressEvent.EVENT_NAME, MapBuilder.of("registrationName", OnPressEvent.EVENT_NAME));
596
- builder.put(OnMarkerSelectEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerSelectEvent.EVENT_NAME));
597
- builder.put(OnMarkerDeselectEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDeselectEvent.EVENT_NAME));
598
- builder.put(OnMarkerDragEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDragEvent.EVENT_NAME));
599
- builder.put(OnMarkerDragStartEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDragStartEvent.EVENT_NAME));
600
- builder.put(OnMarkerDragEndEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMarkerDragEndEvent.EVENT_NAME));
601
- builder.put(OnPoiClickEvent.EVENT_NAME, MapBuilder.of("registrationName", OnPoiClickEvent.EVENT_NAME));
602
- builder.put(OnLongPressEvent.EVENT_NAME, MapBuilder.of("registrationName", OnLongPressEvent.EVENT_NAME));
603
- return builder.build();
604
- }
605
-
606
- public static Map<String, Object> getExportedCustomDirectEventTypeConstants() {
607
- return MapBuilder.of(
608
- OnMapReadyEvent.EVENT_NAME, MapBuilder.of("registrationName", OnMapReadyEvent.EVENT_NAME),
609
- OnUserLocationChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", OnUserLocationChangeEvent.EVENT_NAME),
610
- OnRegionChangeEvent.EVENT_NAME, MapBuilder.of("registrationName", OnRegionChangeEvent.EVENT_NAME)
611
- );
612
- }
613
-
614
-
615
- /*
616
- onDestroy is final method so I can't override it.
617
- */
618
- public synchronized void doDestroy() {
619
- if (destroyed) {
620
- return;
621
- }
622
- destroyed = true;
623
-
624
- if (lifecycleListener != null && context != null) {
625
- context.removeLifecycleEventListener(lifecycleListener);
626
- lifecycleListener = null;
627
- }
628
- if (!paused) {
629
- onPause();
630
- paused = true;
631
- }
632
- onDestroy();
633
- }
634
-
635
- public void setInitialCameraSet(boolean initialCameraSet){
636
- this.initialCameraSet = initialCameraSet;
637
- }
638
-
639
- public void setInitialRegion(ReadableMap initialRegion) {
640
- this.initialRegion = initialRegion;
641
- // Theoretically onMapReady might be called before setInitialRegion
642
- // In that case, trigger moveToRegion manually
643
- if (!initialRegionSet && map != null) {
644
- moveToRegion(initialRegion);
645
- initialRegionSet = true;
646
- }
647
- }
648
-
649
- public void setInitialCamera(ReadableMap initialCamera) {
650
- this.initialCamera = initialCamera;
651
- if (!initialCameraSet && map != null) {
652
- moveToCamera(initialCamera);
653
- initialCameraSet = true;
654
- }
655
- }
656
-
657
- private void applyBridgedProps() {
658
- if(initialRegion != null) {
659
- moveToRegion(initialRegion);
660
- initialRegionSet = true;
661
- } else if(region != null) {
662
- moveToRegion(region);
663
- } else if (initialCamera != null) {
664
- moveToCamera(initialCamera);
665
- initialCameraSet = true;
666
- } else if (camera != null) {
667
- moveToCamera(camera);
668
- }
669
- if(customMapStyleString != null) {
670
- map.setMapStyle(new MapStyleOptions(customMapStyleString));
671
- }
672
- this.setPoiClickEnabled(poiClickEnabled);
673
- }
674
- public static LatLngBounds latLngBoundsFromRegion(ReadableMap region){
675
- if (region == null) return null;
676
-
677
- double lng = region.getDouble("longitude");
678
- double lat = region.getDouble("latitude");
679
- double lngDelta = region.getDouble("longitudeDelta");
680
- double latDelta = region.getDouble("latitudeDelta");
681
- return new LatLngBounds(
682
- new LatLng(lat - latDelta / 2, lng - lngDelta / 2), // southwest
683
- new LatLng(lat + latDelta / 2, lng + lngDelta / 2) // northeast
684
- );
685
- }
686
- private void moveToRegion(ReadableMap region) {
687
- LatLngBounds bounds = latLngBoundsFromRegion(region);
688
- if (bounds == null) return;
689
- double lng = region.getDouble("longitude");
690
- double lat = region.getDouble("latitude");
691
- if (super.getHeight() <= 0 || super.getWidth() <= 0) {
692
- // in this case, our map has not been laid out yet, so we save the bounds in a local
693
- // variable, and make a guess of zoomLevel 10. Not to worry, though: as soon as layout
694
- // occurs, we will move the camera to the saved bounds. Note that if we tried to move
695
- // to the bounds now, it would trigger an exception.
696
- map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(lat, lng), 10));
697
- boundsToMove = bounds;
698
- } else {
699
- map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
700
- boundsToMove = null;
701
- }
702
- }
703
-
704
- public void setRegion(ReadableMap region) {
705
- this.region = region;
706
- if(region != null && map != null) {
707
- moveToRegion(region);
708
- }
709
- }
710
-
711
- public void setCamera(ReadableMap camera) {
712
- this.camera = camera;
713
- if(camera != null && map != null) {
714
- moveToCamera(camera);
715
- }
716
- }
717
- public static CameraPosition cameraPositionFromMap(ReadableMap camera){
718
- if (camera == null) return null;
719
-
720
- CameraPosition.Builder builder = new CameraPosition.Builder();
721
-
722
- ReadableMap center = camera.getMap("center");
723
- if (center != null) {
724
- double lng = center.getDouble("longitude");
725
- double lat = center.getDouble("latitude");
726
- builder.target(new LatLng(lat, lng));
727
- }
728
-
729
- builder.tilt((float)camera.getDouble("pitch"));
730
- builder.bearing((float)camera.getDouble("heading"));
731
- builder.zoom((float)camera.getDouble("zoom"));
732
-
733
- return builder.build();
734
- }
735
- public void moveToCamera(ReadableMap cameraMap) {
736
- CameraPosition camera = cameraPositionFromMap(cameraMap);
737
- if (camera == null) return;
738
- CameraUpdate update = CameraUpdateFactory.newCameraPosition(camera);
739
-
740
- if (super.getHeight() <= 0 || super.getWidth() <= 0) {
741
- // in this case, our map has not been laid out yet, so we save the camera update in a
742
- // local variable. As soon as layout occurs, we will move the camera to the saved update.
743
- // Note that if we tried to move to the camera now, it would trigger an exception.
744
- cameraToSet = update;
745
- } else {
746
- map.moveCamera(update);
747
- cameraToSet = null;
748
- }
749
- }
750
-
751
- public void setMapStyle(@Nullable String customMapStyleString) {
752
- this.customMapStyleString = customMapStyleString;
753
- if(map != null && customMapStyleString != null) {
754
- map.setMapStyle(new MapStyleOptions(customMapStyleString));
755
- }
756
- }
757
-
758
- public void setShowsUserLocation(boolean showUserLocation) {
759
- this.showUserLocation = showUserLocation; // hold onto this for lifecycle handling
760
- if (hasPermissions()) {
761
- map.setLocationSource(fusedLocationSource);
762
- //noinspection MissingPermission
763
- map.setMyLocationEnabled(showUserLocation);
764
- }
765
- }
766
-
767
- public void setUserLocationPriority(int priority){
768
- fusedLocationSource.setPriority(priority);
769
- }
770
-
771
- public void setUserLocationUpdateInterval(int interval){
772
- fusedLocationSource.setInterval(interval);
773
- }
774
-
775
- public void setUserLocationFastestInterval(int interval){
776
- fusedLocationSource.setFastestInterval(interval);
777
- }
778
-
779
- public void setShowsMyLocationButton(boolean showMyLocationButton) {
780
- if (map != null) {
781
- if (hasPermissions() || !showMyLocationButton) {
782
- map.getUiSettings().setMyLocationButtonEnabled(showMyLocationButton);
783
- }
784
- }
785
- }
786
-
787
- public void setToolbarEnabled(boolean toolbarEnabled) {
788
- if (map != null) {
789
- if (hasPermissions() || !toolbarEnabled) {
790
- map.getUiSettings().setMapToolbarEnabled(toolbarEnabled);
791
- }
792
- }
793
- }
794
- public void setMapType(int mapType){
795
- if (map != null) {
796
- map.setMapType(mapType);
797
- }
798
- }
799
-
800
- public void setCacheEnabled(boolean cacheEnabled) {
801
- this.cacheEnabled = cacheEnabled;
802
- this.cacheView();
803
- }
804
-
805
- public void setPoiClickEnabled(boolean poiClickEnabled) {
806
- this.poiClickEnabled = poiClickEnabled;
807
- if (map != null) {
808
- map.setOnPoiClickListener(poiClickEnabled ? this : null);
809
- }
810
- }
811
-
812
- public void setLoadingEnabled(boolean loadingEnabled) {
813
- if (loadingEnabled && !this.isMapLoaded) {
814
- this.getMapLoadingLayoutView().setVisibility(View.VISIBLE);
815
- }
816
- }
817
-
818
- public void setMoveOnMarkerPress(boolean moveOnPress) {
819
- this.moveOnMarkerPress = moveOnPress;
820
- }
821
- public void setMaxZoomLevel(float maxZoomLevel){
822
- this.maxZoomLevel = maxZoomLevel;
823
- if (map != null) {
824
- map.setMaxZoomPreference(maxZoomLevel);
825
- }
826
- }
827
- public void setMinZoomLevel(float minZoomLevel){
828
- this.minZoomLevel = minZoomLevel;
829
- if (map != null) {
830
- map.setMinZoomPreference(minZoomLevel);
831
- }
832
- }
833
- public void setPitchEnabled(boolean pitchEnabled){
834
- this.pitchEnabled = pitchEnabled;
835
- if (map != null) {
836
- map.getUiSettings().setTiltGesturesEnabled(pitchEnabled);
837
- }
838
- }
839
- public void setShowsCompass(boolean showsCompass){
840
- this.showsCompass = showsCompass;
841
- if (map != null) {
842
- map.getUiSettings().setCompassEnabled(showsCompass);
843
- }
844
- }
845
- public void setRotateEnabled(boolean rotateEnabled){
846
- this.rotateEnabled = rotateEnabled;
847
- if (map != null) {
848
- map.getUiSettings().setRotateGesturesEnabled(rotateEnabled);
849
- }
850
- }
851
- public void setZoomEnabled(boolean zoomEnabled){
852
- this.zoomEnabled = zoomEnabled;
853
- if (map != null) {
854
- map.getUiSettings().setZoomGesturesEnabled(zoomEnabled);
855
- }
856
- }
857
- public void setZoomControlEnabled(boolean zoomControlEnabled){
858
- this.zoomControlEnabled = zoomControlEnabled;
859
- if (map != null) {
860
- map.getUiSettings().setZoomControlsEnabled(zoomControlEnabled);
861
- }
862
- }
863
- public void setScrollDuringRotateOrZoomEnabled(boolean scrollDuringRotateOrZoomEnabled){
864
- this.scrollDuringRotateOrZoomEnabled = scrollDuringRotateOrZoomEnabled;
865
- if (map != null) {
866
- map.getUiSettings().setScrollGesturesEnabledDuringRotateOrZoom(scrollDuringRotateOrZoomEnabled);
867
- }
868
- }
869
- public void setScrollEnabled(boolean scrollEnabled){
870
- this.scrollEnabled = scrollEnabled;
871
- if (map != null) {
872
- map.getUiSettings().setScrollGesturesEnabled(scrollEnabled);
873
- }
874
- }
875
- public void setShowIndoors(boolean showIndoors){
876
- this.showIndoors = showIndoors;
877
- if (map != null) {
878
- map.setIndoorEnabled(showIndoors);
879
- }
880
- }
881
- public void setShowsIndoorLevelPicker(boolean showsIndoorLevelPicker) {
882
- this.showsIndoorLevelPicker = showsIndoorLevelPicker;
883
- if (map != null) {
884
- map.getUiSettings().setIndoorLevelPickerEnabled(showsIndoorLevelPicker);
885
- }
886
- }
887
- public void setShowBuildings(boolean showBuildings) {
888
- this.setShowBuildings = showBuildings;
889
- if (map != null) {
890
- map.setBuildingsEnabled(showBuildings);
891
- }
892
- }
893
-
894
-
895
- public void setLoadingBackgroundColor(Integer loadingBackgroundColor) {
896
- this.loadingBackgroundColor = loadingBackgroundColor;
897
-
898
- if (this.mapLoadingLayout != null) {
899
- if (loadingBackgroundColor == null) {
900
- this.mapLoadingLayout.setBackgroundColor(Color.WHITE);
901
- } else {
902
- this.mapLoadingLayout.setBackgroundColor(this.loadingBackgroundColor);
903
- }
904
- }
905
- }
906
-
907
- public void setLoadingIndicatorColor(Integer loadingIndicatorColor) {
908
- this.loadingIndicatorColor = loadingIndicatorColor;
909
- if (this.mapLoadingProgressBar != null) {
910
- Integer color = loadingIndicatorColor;
911
- if (color == null) {
912
- color = Color.parseColor("#606060");
913
- }
914
-
915
- ColorStateList progressTintList = ColorStateList.valueOf(loadingIndicatorColor);
916
- ColorStateList secondaryProgressTintList = ColorStateList.valueOf(loadingIndicatorColor);
917
- ColorStateList indeterminateTintList = ColorStateList.valueOf(loadingIndicatorColor);
918
-
919
- this.mapLoadingProgressBar.setProgressTintList(progressTintList);
920
- this.mapLoadingProgressBar.setSecondaryProgressTintList(secondaryProgressTintList);
921
- this.mapLoadingProgressBar.setIndeterminateTintList(indeterminateTintList);
922
- }
923
- }
924
-
925
- public void setHandlePanDrag(boolean handlePanDrag) {
926
- this.handlePanDrag = handlePanDrag;
927
- }
928
-
929
- public void addFeature(View child, int index) {
930
- // Our desired API is to pass up annotations/overlays as children to the mapview component.
931
- // This is where we intercept them and do the appropriate underlying mapview action.
932
- if (child instanceof MapMarker) {
933
- MapMarker annotation = (MapMarker) child;
934
- annotation.addToMap(markerCollection);
935
- features.add(index, annotation);
936
-
937
- // Allow visibility event to be triggered later
938
- int visibility = annotation.getVisibility();
939
- annotation.setVisibility(INVISIBLE);
940
-
941
- // Remove from a view group if already present, prevent "specified child
942
- // already had a parent" error.
943
- ViewGroup annotationParent = (ViewGroup)annotation.getParent();
944
- if (annotationParent != null) {
945
- annotationParent.removeView(annotation);
946
- }
947
-
948
- // Add to the parent group
949
- attacherGroup.addView(annotation);
950
-
951
- // Trigger visibility event if necessary.
952
- // With some testing, seems like it is not always
953
- // triggered just by being added to a parent view.
954
- annotation.setVisibility(visibility);
955
-
956
- Marker marker = (Marker) annotation.getFeature();
957
- markerMap.put(marker, annotation);
958
- } else if (child instanceof MapPolyline) {
959
- MapPolyline polylineView = (MapPolyline) child;
960
- polylineView.addToMap(polylineCollection);
961
- features.add(index, polylineView);
962
- Polyline polyline = (Polyline) polylineView.getFeature();
963
- polylineMap.put(polyline, polylineView);
964
- } else if (child instanceof MapGradientPolyline) {
965
- MapGradientPolyline polylineView = (MapGradientPolyline) child;
966
- polylineView.addToMap(map);
967
- features.add(index, polylineView);
968
- TileOverlay tileOverlay = (TileOverlay) polylineView.getFeature();
969
- gradientPolylineMap.put(tileOverlay, polylineView);
970
- } else if (child instanceof MapPolygon) {
971
- MapPolygon polygonView = (MapPolygon) child;
972
- polygonView.addToMap(polygonCollection);
973
- features.add(index, polygonView);
974
- Polygon polygon = (Polygon) polygonView.getFeature();
975
- polygonMap.put(polygon, polygonView);
976
- } else if (child instanceof MapCircle) {
977
- MapCircle circleView = (MapCircle) child;
978
- circleView.addToMap(circleCollection);
979
- features.add(index, circleView);
980
- } else if (child instanceof MapUrlTile) {
981
- MapUrlTile urlTileView = (MapUrlTile) child;
982
- urlTileView.addToMap(map);
983
- features.add(index, urlTileView);
984
- } else if (child instanceof MapWMSTile) {
985
- MapWMSTile urlTileView = (MapWMSTile) child;
986
- urlTileView.addToMap(map);
987
- features.add(index, urlTileView);
988
- } else if (child instanceof MapLocalTile) {
989
- MapLocalTile localTileView = (MapLocalTile) child;
990
- localTileView.addToMap(map);
991
- features.add(index, localTileView);
992
- } else if (child instanceof MapOverlay) {
993
- MapOverlay overlayView = (MapOverlay) child;
994
- overlayView.addToMap(groundOverlayCollection);
995
- features.add(index, overlayView);
996
- GroundOverlay overlay = (GroundOverlay) overlayView.getFeature();
997
- overlayMap.put(overlay, overlayView);
998
- } else if (child instanceof MapHeatmap) {
999
- MapHeatmap heatmapView = (MapHeatmap) child;
1000
- heatmapView.addToMap(map);
1001
- features.add(index, heatmapView);
1002
- TileOverlay heatmap = (TileOverlay)heatmapView.getFeature();
1003
- heatmapMap.put(heatmap, heatmapView);
1004
- } else if (child instanceof ViewGroup) {
1005
- ViewGroup children = (ViewGroup) child;
1006
- for (int i = 0; i < children.getChildCount(); i++) {
1007
- addFeature(children.getChildAt(i), index);
1008
- }
1009
- } else {
1010
- addView(child, index);
1011
- }
1012
- }
1013
-
1014
- public int getFeatureCount() {
1015
- return features.size();
1016
- }
1017
-
1018
- public View getFeatureAt(int index) {
1019
- return features.get(index);
1020
- }
1021
-
1022
- public void removeFeatureAt(int index) {
1023
- MapFeature feature = features.remove(index);
1024
- if (feature instanceof MapMarker) {
1025
- markerMap.remove(feature.getFeature());
1026
- feature.removeFromMap(markerCollection);
1027
- attacherGroup.removeView(feature);
1028
- } else if (feature instanceof MapHeatmap) {
1029
- heatmapMap.remove(feature.getFeature());
1030
- feature.removeFromMap(map);
1031
- } else if(feature instanceof MapCircle) {
1032
- feature.removeFromMap(circleCollection);
1033
- } else if(feature instanceof MapOverlay) {
1034
- feature.removeFromMap(groundOverlayCollection);
1035
- } else if(feature instanceof MapPolygon) {
1036
- feature.removeFromMap(polygonCollection);
1037
- } else if(feature instanceof MapPolyline) {
1038
- feature.removeFromMap(polylineCollection);
1039
- } else {
1040
- feature.removeFromMap(map);
1041
- }
1042
- }
1043
-
1044
- public WritableMap makeClickEventData(LatLng point) {
1045
- WritableMap event = new WritableNativeMap();
1046
-
1047
- WritableMap coordinate = new WritableNativeMap();
1048
- coordinate.putDouble("latitude", point.latitude);
1049
- coordinate.putDouble("longitude", point.longitude);
1050
- event.putMap("coordinate", coordinate);
1051
-
1052
- Projection projection = map.getProjection();
1053
- Point screenPoint = projection.toScreenLocation(point);
1054
-
1055
- WritableMap position = new WritableNativeMap();
1056
- position.putDouble("x", screenPoint.x);
1057
- position.putDouble("y", screenPoint.y);
1058
- event.putMap("position", position);
1059
-
1060
- return event;
1061
- }
1062
-
1063
- public void updateExtraData(Object extraData) {
1064
- if (setPaddingDeferred && super.getHeight() > 0 && super.getWidth() > 0) {
1065
- CameraUpdate cu = CameraUpdateFactory.newCameraPosition(map.getCameraPosition());
1066
-
1067
- map.setPadding(edgeLeftPadding + baseLeftMapPadding,
1068
- edgeTopPadding + baseTopMapPadding,
1069
- edgeRightPadding + baseRightMapPadding,
1070
- edgeBottomPadding + baseBottomMapPadding);
1071
- map.moveCamera(cu);
1072
-
1073
- // Move the google logo to the default base padding value.
1074
- map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1075
-
1076
- setPaddingDeferred = false;
1077
- }
1078
-
1079
- // if boundsToMove is not null, we now have the MapView's width/height, so we can apply
1080
- // a proper camera move
1081
- if (boundsToMove != null) {
1082
- HashMap<String, Float> data = (HashMap<String, Float>) extraData;
1083
- int width = data.get("width") == null ? 0 : data.get("width").intValue();
1084
- int height = data.get("height") == null ? 0 : data.get("height").intValue();
1085
-
1086
- //fix for https://github.com/react-native-maps/react-native-maps/issues/245,
1087
- //it's not guaranteed the passed-in height and width would be greater than 0.
1088
- if (width <= 0 || height <= 0) {
1089
- map.moveCamera(CameraUpdateFactory.newLatLngBounds(boundsToMove, 0));
1090
- } else {
1091
- map.moveCamera(CameraUpdateFactory.newLatLngBounds(boundsToMove, width, height, 0));
1092
- }
1093
-
1094
- boundsToMove = null;
1095
- cameraToSet = null;
1096
- }
1097
- else if (cameraToSet != null) {
1098
- map.moveCamera(cameraToSet);
1099
- cameraToSet = null;
1100
- }
1101
- }
1102
-
1103
- public void animateToCamera(ReadableMap camera, int duration) {
1104
- if (map == null) return;
1105
- CameraPosition.Builder builder = new CameraPosition.Builder(map.getCameraPosition());
1106
- if (camera.hasKey("zoom")) {
1107
- builder.zoom((float)camera.getDouble("zoom"));
1108
- }
1109
- if (camera.hasKey("heading")) {
1110
- builder.bearing((float)camera.getDouble("heading"));
1138
+ setPaddingDeferred = false;
1139
+ }
1140
+
1141
+ // if boundsToMove is not null, we now have the MapView's width/height, so we can apply
1142
+ // a proper camera move
1143
+ if (boundsToMove != null) {
1144
+ HashMap<String, Float> data = (HashMap<String, Float>) extraData;
1145
+ int width = data.get("width") == null ? 0 : data.get("width").intValue();
1146
+ int height = data.get("height") == null ? 0 : data.get("height").intValue();
1147
+
1148
+ //fix for https://github.com/react-native-maps/react-native-maps/issues/245,
1149
+ //it's not guaranteed the passed-in height and width would be greater than 0.
1150
+ if (width <= 0 || height <= 0) {
1151
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(boundsToMove, 0));
1152
+ } else {
1153
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(boundsToMove, width, height, 0));
1154
+ }
1155
+
1156
+ boundsToMove = null;
1157
+ cameraToSet = null;
1158
+ } else if (cameraToSet != null) {
1159
+ map.moveCamera(cameraToSet);
1160
+ cameraToSet = null;
1161
+ }
1111
1162
  }
1112
- if (camera.hasKey("pitch")) {
1113
- builder.tilt((float)camera.getDouble("pitch"));
1114
- }
1115
- if (camera.hasKey("center")) {
1116
- ReadableMap center = camera.getMap("center");
1117
- builder.target(new LatLng(center.getDouble("latitude"), center.getDouble("longitude")));
1118
- }
1119
-
1120
- CameraUpdate update = CameraUpdateFactory.newCameraPosition(builder.build());
1121
-
1122
- if (duration <= 0) {
1123
- map.moveCamera(update);
1124
- }
1125
- else {
1126
- map.animateCamera(update, duration, null);
1163
+ public void animateToCamera(CameraPosition position, int duration) {
1164
+ if (map == null) return;
1165
+ CameraUpdate update = CameraUpdateFactory.newCameraPosition(position);
1166
+ if (duration <= 0) {
1167
+ map.moveCamera(update);
1168
+ } else {
1169
+ map.animateCamera(update, duration, null);
1170
+ }
1127
1171
  }
1128
- }
1129
1172
 
1130
- public void animateToRegion(LatLngBounds bounds, int duration) {
1131
- if (map == null) return;
1132
- if(duration <= 0) {
1133
- map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
1134
- } else {
1135
- map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null);
1173
+ public void animateToCamera(ReadableMap camera, int duration) {
1174
+ if (map == null) return;
1175
+ CameraPosition.Builder builder = new CameraPosition.Builder(map.getCameraPosition());
1176
+ if (camera.hasKey("zoom")) {
1177
+ builder.zoom((float) camera.getDouble("zoom"));
1178
+ }
1179
+ if (camera.hasKey("heading")) {
1180
+ builder.bearing((float) camera.getDouble("heading"));
1181
+ }
1182
+ if (camera.hasKey("pitch")) {
1183
+ builder.tilt((float) camera.getDouble("pitch"));
1184
+ }
1185
+ if (camera.hasKey("center")) {
1186
+ ReadableMap center = camera.getMap("center");
1187
+ builder.target(new LatLng(center.getDouble("latitude"), center.getDouble("longitude")));
1188
+ }
1189
+ animateToCamera(builder.build(), duration);
1190
+ }
1191
+
1192
+ public void animateToRegion(LatLngBounds bounds, int duration) {
1193
+ if (map == null) return;
1194
+ if (duration <= 0) {
1195
+ map.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0));
1196
+ } else {
1197
+ map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 0), duration, null);
1198
+ }
1136
1199
  }
1137
- }
1138
1200
 
1139
- public void fitToElements(ReadableMap edgePadding, boolean animated) {
1140
- if (map == null) return;
1201
+ public void fitToElements(ReadableMap edgePadding, boolean animated) {
1202
+ if (map == null) return;
1141
1203
 
1142
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
1204
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
1143
1205
 
1144
- boolean addedPosition = false;
1206
+ boolean addedPosition = false;
1145
1207
 
1146
- for (MapFeature feature : features) {
1147
- if (feature instanceof MapMarker) {
1148
- Marker marker = (Marker) feature.getFeature();
1149
- builder.include(marker.getPosition());
1150
- addedPosition = true;
1151
- }
1152
- // TODO(lmr): may want to include shapes / etc.
1208
+ for (MapFeature feature : features) {
1209
+ if (feature instanceof MapMarker) {
1210
+ Marker marker = (Marker) feature.getFeature();
1211
+ builder.include(marker.getPosition());
1212
+ addedPosition = true;
1213
+ }
1214
+ // TODO(lmr): may want to include shapes / etc.
1215
+ }
1216
+ if (addedPosition) {
1217
+ LatLngBounds bounds = builder.build();
1218
+ CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
1219
+
1220
+ if (edgePadding != null) {
1221
+ appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"),
1222
+ edgePadding.getInt("right"), edgePadding.getInt("bottom"));
1223
+ }
1224
+
1225
+ if (animated) {
1226
+ map.animateCamera(cu);
1227
+ } else {
1228
+ map.moveCamera(cu);
1229
+ }
1230
+ // Move the google logo to the default base padding value.
1231
+ map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1232
+ }
1153
1233
  }
1154
- if (addedPosition) {
1155
- LatLngBounds bounds = builder.build();
1156
- CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
1157
1234
 
1158
- if (edgePadding != null) {
1159
- appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"),
1160
- edgePadding.getInt("right"), edgePadding.getInt("bottom"));
1161
- }
1235
+ public void fitToSuppliedMarkers(ReadableArray markerIDsArray, ReadableMap edgePadding, boolean animated) {
1236
+ if (map == null) return;
1237
+
1238
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
1239
+
1240
+ String[] markerIDs = new String[markerIDsArray.size()];
1241
+ for (int i = 0; i < markerIDsArray.size(); i++) {
1242
+ markerIDs[i] = markerIDsArray.getString(i);
1243
+ }
1244
+
1245
+ boolean addedPosition = false;
1162
1246
 
1163
- if (animated) {
1164
- map.animateCamera(cu);
1165
- } else {
1247
+ List<String> markerIDList = Arrays.asList(markerIDs);
1248
+
1249
+ for (MapFeature feature : features) {
1250
+ if (feature instanceof MapMarker) {
1251
+ String identifier = ((MapMarker) feature).getIdentifier();
1252
+ Marker marker = (Marker) feature.getFeature();
1253
+ if (markerIDList.contains(identifier)) {
1254
+ builder.include(marker.getPosition());
1255
+ addedPosition = true;
1256
+ }
1257
+ }
1258
+ }
1259
+
1260
+ if (addedPosition) {
1261
+ LatLngBounds bounds = builder.build();
1262
+ CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
1263
+
1264
+ if (edgePadding != null) {
1265
+ appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"),
1266
+ edgePadding.getInt("right"), edgePadding.getInt("bottom"));
1267
+ }
1268
+
1269
+ if (animated) {
1270
+ map.animateCamera(cu);
1271
+ } else {
1272
+ map.moveCamera(cu);
1273
+ }
1274
+ // Move the google logo to the default base padding value.
1275
+ map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1276
+ }
1277
+ }
1278
+
1279
+ // padding configured by 'mapPadding' property
1280
+ int baseLeftMapPadding;
1281
+ int baseRightMapPadding;
1282
+ int baseTopMapPadding;
1283
+ int baseBottomMapPadding;
1284
+ // extra padding specified by 'edgePadding' option of fitToElements/fitToSuppliedMarkers/fitToCoordinates
1285
+ int edgeLeftPadding;
1286
+ int edgeRightPadding;
1287
+ int edgeTopPadding;
1288
+ int edgeBottomPadding;
1289
+
1290
+ public void applyBaseMapPadding(int left, int top, int right, int bottom) {
1291
+ if (super.getHeight() <= 0 || super.getWidth() <= 0) {
1292
+ // the map is not laid out yet and calling setPadding() now has no effect
1293
+ baseLeftMapPadding = left;
1294
+ baseRightMapPadding = right;
1295
+ baseTopMapPadding = top;
1296
+ baseBottomMapPadding = bottom;
1297
+ setPaddingDeferred = true;
1298
+ return;
1299
+ }
1300
+
1301
+ // retrieve current camera with current edge paddings configured
1302
+ map.setPadding(edgeLeftPadding + baseLeftMapPadding,
1303
+ edgeTopPadding + baseTopMapPadding,
1304
+ edgeRightPadding + baseRightMapPadding,
1305
+ edgeBottomPadding + baseBottomMapPadding);
1306
+ CameraUpdate cu = CameraUpdateFactory.newCameraPosition(map.getCameraPosition());
1307
+
1308
+ baseLeftMapPadding = left;
1309
+ baseRightMapPadding = right;
1310
+ baseTopMapPadding = top;
1311
+ baseBottomMapPadding = bottom;
1312
+
1313
+ // apply base paddings and restore center position of the map
1314
+ map.setPadding(edgeLeftPadding + left,
1315
+ edgeTopPadding + top,
1316
+ edgeRightPadding + right,
1317
+ edgeBottomPadding + bottom);
1166
1318
  map.moveCamera(cu);
1167
- }
1168
- // Move the google logo to the default base padding value.
1169
- map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1319
+
1320
+ // Move the google logo to the default base padding value.
1321
+ map.setPadding(left, top, right, bottom);
1322
+ }
1323
+
1324
+ public void fitToCoordinates(ReadableArray coordinatesArray, ReadableMap edgePadding,
1325
+ boolean animated) {
1326
+ if (map == null) return;
1327
+
1328
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
1329
+
1330
+ for (int i = 0; i < coordinatesArray.size(); i++) {
1331
+ ReadableMap latLng = coordinatesArray.getMap(i);
1332
+ double lat = latLng.getDouble("latitude");
1333
+ double lng = latLng.getDouble("longitude");
1334
+ builder.include(new LatLng(lat, lng));
1335
+ }
1336
+
1337
+ LatLngBounds bounds = builder.build();
1338
+ CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
1339
+
1340
+ if (edgePadding != null) {
1341
+ appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), edgePadding.getInt("right"), edgePadding.getInt("bottom"));
1342
+ }
1343
+
1344
+ if (animated) {
1345
+ map.animateCamera(cu);
1346
+ } else {
1347
+ map.moveCamera(cu);
1348
+ }
1349
+ // Move the google logo to the default base padding value.
1350
+ map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1170
1351
  }
1171
- }
1172
1352
 
1173
- public void fitToSuppliedMarkers(ReadableArray markerIDsArray, ReadableMap edgePadding, boolean animated) {
1174
- if (map == null) return;
1353
+ private void appendMapPadding(int iLeft, int iTop, int iRight, int iBottom) {
1354
+ double density = getResources().getDisplayMetrics().density;
1355
+
1356
+ edgeLeftPadding = (int) (iLeft * density);
1357
+ edgeTopPadding = (int) (iTop * density);
1358
+ edgeRightPadding = (int) (iRight * density);
1359
+ edgeBottomPadding = (int) (iBottom * density);
1360
+
1361
+ map.setPadding(edgeLeftPadding + baseLeftMapPadding,
1362
+ edgeTopPadding + baseTopMapPadding,
1363
+ edgeRightPadding + baseRightMapPadding,
1364
+ edgeBottomPadding + baseBottomMapPadding);
1365
+ }
1175
1366
 
1176
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
1367
+ public double[][] getMapBoundaries() {
1368
+ LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
1369
+ LatLng northEast = bounds.northeast;
1370
+ LatLng southWest = bounds.southwest;
1177
1371
 
1178
- String[] markerIDs = new String[markerIDsArray.size()];
1179
- for (int i = 0; i < markerIDsArray.size(); i++) {
1180
- markerIDs[i] = markerIDsArray.getString(i);
1372
+ return new double[][]{
1373
+ {northEast.longitude, northEast.latitude},
1374
+ {southWest.longitude, southWest.latitude}
1375
+ };
1181
1376
  }
1182
1377
 
1183
- boolean addedPosition = false;
1378
+ public void setMapBoundaries(ReadableMap northEast, ReadableMap southWest) {
1379
+ if (map == null) return;
1380
+
1381
+ LatLngBounds.Builder builder = new LatLngBounds.Builder();
1184
1382
 
1185
- List<String> markerIDList = Arrays.asList(markerIDs);
1383
+ double latNE = northEast.getDouble("latitude");
1384
+ double lngNE = northEast.getDouble("longitude");
1385
+ builder.include(new LatLng(latNE, lngNE));
1186
1386
 
1187
- for (MapFeature feature : features) {
1188
- if (feature instanceof MapMarker) {
1189
- String identifier = ((MapMarker) feature).getIdentifier();
1190
- Marker marker = (Marker) feature.getFeature();
1191
- if (markerIDList.contains(identifier)) {
1192
- builder.include(marker.getPosition());
1193
- addedPosition = true;
1387
+ double latSW = southWest.getDouble("latitude");
1388
+ double lngSW = southWest.getDouble("longitude");
1389
+ builder.include(new LatLng(latSW, lngSW));
1390
+
1391
+ LatLngBounds bounds = builder.build();
1392
+
1393
+ map.setLatLngBoundsForCameraTarget(bounds);
1394
+ }
1395
+
1396
+ // InfoWindowAdapter interface
1397
+
1398
+ @Override
1399
+ public View getInfoWindow(Marker marker) {
1400
+ MapMarker markerView = getMarkerMap(marker);
1401
+ return markerView.getCallout();
1402
+ }
1403
+
1404
+ @Override
1405
+ public View getInfoContents(Marker marker) {
1406
+ MapMarker markerView = getMarkerMap(marker);
1407
+ return markerView.getInfoContents();
1408
+ }
1409
+
1410
+ @Override
1411
+ public boolean dispatchTouchEvent(MotionEvent ev) {
1412
+ gestureDetector.onTouchEvent(ev);
1413
+
1414
+ int X = (int) ev.getX();
1415
+ int Y = (int) ev.getY();
1416
+ if (map != null) {
1417
+ tapLocation = map.getProjection().fromScreenLocation(new Point(X, Y));
1418
+ }
1419
+
1420
+ int action = MotionEventCompat.getActionMasked(ev);
1421
+
1422
+ switch (action) {
1423
+ case (MotionEvent.ACTION_DOWN):
1424
+ this.getParent().requestDisallowInterceptTouchEvent(
1425
+ map != null && map.getUiSettings().isScrollGesturesEnabled());
1426
+ break;
1427
+ case (MotionEvent.ACTION_UP):
1428
+ // Clear this regardless, since isScrollGesturesEnabled() may have been updated
1429
+ this.getParent().requestDisallowInterceptTouchEvent(false);
1430
+ break;
1194
1431
  }
1195
- }
1432
+ super.dispatchTouchEvent(ev);
1433
+ return true;
1196
1434
  }
1197
1435
 
1198
- if (addedPosition) {
1199
- LatLngBounds bounds = builder.build();
1200
- CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
1436
+ @Override
1437
+ public void onMarkerDragStart(Marker marker) {
1438
+ WritableMap event = makeClickEventData(marker.getPosition());
1439
+ // todo: use Fabric events
1440
+ dispatchEvent(event, OnMarkerDragStartEvent::new);
1201
1441
 
1202
- if (edgePadding != null) {
1203
- appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"),
1204
- edgePadding.getInt("right"), edgePadding.getInt("bottom"));
1205
- }
1442
+ MapMarker markerView = getMarkerMap(marker);
1443
+ event = makeClickEventData(marker.getPosition());
1444
+ // todo: use Fabric events
1445
+ // manager.pushEvent(context, markerView, "onDragStart", event);
1446
+ }
1206
1447
 
1207
- if (animated) {
1208
- map.animateCamera(cu);
1209
- } else {
1210
- map.moveCamera(cu);
1211
- }
1212
- // Move the google logo to the default base padding value.
1213
- map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1214
- }
1215
- }
1216
-
1217
- // padding configured by 'mapPadding' property
1218
- int baseLeftMapPadding;
1219
- int baseRightMapPadding;
1220
- int baseTopMapPadding;
1221
- int baseBottomMapPadding;
1222
- // extra padding specified by 'edgePadding' option of fitToElements/fitToSuppliedMarkers/fitToCoordinates
1223
- int edgeLeftPadding;
1224
- int edgeRightPadding;
1225
- int edgeTopPadding;
1226
- int edgeBottomPadding;
1227
-
1228
- public void applyBaseMapPadding(int left, int top, int right, int bottom){
1229
- if (super.getHeight() <= 0 || super.getWidth() <= 0) {
1230
- // the map is not laid out yet and calling setPadding() now has no effect
1231
- baseLeftMapPadding = left;
1232
- baseRightMapPadding = right;
1233
- baseTopMapPadding = top;
1234
- baseBottomMapPadding = bottom;
1235
- setPaddingDeferred = true;
1236
- return;
1237
- }
1238
-
1239
- // retrieve current camera with current edge paddings configured
1240
- map.setPadding(edgeLeftPadding + baseLeftMapPadding,
1241
- edgeTopPadding + baseTopMapPadding,
1242
- edgeRightPadding + baseRightMapPadding,
1243
- edgeBottomPadding + baseBottomMapPadding);
1244
- CameraUpdate cu = CameraUpdateFactory.newCameraPosition(map.getCameraPosition());
1245
-
1246
- baseLeftMapPadding = left;
1247
- baseRightMapPadding = right;
1248
- baseTopMapPadding = top;
1249
- baseBottomMapPadding = bottom;
1250
-
1251
- // apply base paddings and restore center position of the map
1252
- map.setPadding(edgeLeftPadding + left,
1253
- edgeTopPadding + top,
1254
- edgeRightPadding + right,
1255
- edgeBottomPadding + bottom);
1256
- map.moveCamera(cu);
1257
-
1258
- // Move the google logo to the default base padding value.
1259
- map.setPadding(left, top, right, bottom);
1260
- }
1261
-
1262
- public void fitToCoordinates(ReadableArray coordinatesArray, ReadableMap edgePadding,
1263
- boolean animated) {
1264
- if (map == null) return;
1265
-
1266
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
1267
-
1268
- for (int i = 0; i < coordinatesArray.size(); i++) {
1269
- ReadableMap latLng = coordinatesArray.getMap(i);
1270
- double lat = latLng.getDouble("latitude");
1271
- double lng = latLng.getDouble("longitude");
1272
- builder.include(new LatLng(lat, lng));
1273
- }
1274
-
1275
- LatLngBounds bounds = builder.build();
1276
- CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, 0);
1277
-
1278
- if (edgePadding != null) {
1279
- appendMapPadding(edgePadding.getInt("left"), edgePadding.getInt("top"), edgePadding.getInt("right"), edgePadding.getInt("bottom"));
1280
- }
1281
-
1282
- if (animated) {
1283
- map.animateCamera(cu);
1284
- } else {
1285
- map.moveCamera(cu);
1286
- }
1287
- // Move the google logo to the default base padding value.
1288
- map.setPadding(baseLeftMapPadding, baseTopMapPadding, baseRightMapPadding, baseBottomMapPadding);
1289
- }
1290
-
1291
- private void appendMapPadding(int iLeft,int iTop, int iRight, int iBottom) {
1292
- double density = getResources().getDisplayMetrics().density;
1293
-
1294
- edgeLeftPadding = (int) (iLeft * density);
1295
- edgeTopPadding = (int) (iTop * density);
1296
- edgeRightPadding = (int) (iRight * density);
1297
- edgeBottomPadding = (int) (iBottom * density);
1298
-
1299
- map.setPadding(edgeLeftPadding + baseLeftMapPadding,
1300
- edgeTopPadding + baseTopMapPadding,
1301
- edgeRightPadding + baseRightMapPadding,
1302
- edgeBottomPadding + baseBottomMapPadding);
1303
- }
1304
-
1305
- public double[][] getMapBoundaries() {
1306
- LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds;
1307
- LatLng northEast = bounds.northeast;
1308
- LatLng southWest = bounds.southwest;
1309
-
1310
- return new double[][] {
1311
- {northEast.longitude, northEast.latitude},
1312
- {southWest.longitude, southWest.latitude}
1313
- };
1314
- }
1315
-
1316
- public void setMapBoundaries(ReadableMap northEast, ReadableMap southWest) {
1317
- if (map == null) return;
1318
-
1319
- LatLngBounds.Builder builder = new LatLngBounds.Builder();
1320
-
1321
- double latNE = northEast.getDouble("latitude");
1322
- double lngNE = northEast.getDouble("longitude");
1323
- builder.include(new LatLng(latNE, lngNE));
1324
-
1325
- double latSW = southWest.getDouble("latitude");
1326
- double lngSW = southWest.getDouble("longitude");
1327
- builder.include(new LatLng(latSW, lngSW));
1448
+ @Override
1449
+ public void onMarkerDrag(Marker marker) {
1450
+ WritableMap event = makeClickEventData(marker.getPosition());
1451
+ dispatchEvent(event, OnMarkerDragEvent::new);
1328
1452
 
1329
- LatLngBounds bounds = builder.build();
1330
-
1331
- map.setLatLngBoundsForCameraTarget(bounds);
1332
- }
1333
-
1334
- // InfoWindowAdapter interface
1335
-
1336
- @Override
1337
- public View getInfoWindow(Marker marker) {
1338
- MapMarker markerView = getMarkerMap(marker);
1339
- return markerView.getCallout();
1340
- }
1341
-
1342
- @Override
1343
- public View getInfoContents(Marker marker) {
1344
- MapMarker markerView = getMarkerMap(marker);
1345
- return markerView.getInfoContents();
1346
- }
1347
-
1348
- @Override
1349
- public boolean dispatchTouchEvent(MotionEvent ev) {
1350
- gestureDetector.onTouchEvent(ev);
1351
-
1352
- int X = (int)ev.getX();
1353
- int Y = (int)ev.getY();
1354
- if(map != null) {
1355
- tapLocation = map.getProjection().fromScreenLocation(new Point(X,Y));
1356
- }
1357
-
1358
- int action = MotionEventCompat.getActionMasked(ev);
1359
-
1360
- switch (action) {
1361
- case (MotionEvent.ACTION_DOWN):
1362
- this.getParent().requestDisallowInterceptTouchEvent(
1363
- map != null && map.getUiSettings().isScrollGesturesEnabled());
1364
- break;
1365
- case (MotionEvent.ACTION_UP):
1366
- // Clear this regardless, since isScrollGesturesEnabled() may have been updated
1367
- this.getParent().requestDisallowInterceptTouchEvent(false);
1368
- break;
1369
- }
1370
- super.dispatchTouchEvent(ev);
1371
- return true;
1372
- }
1373
-
1374
- @Override
1375
- public void onMarkerDragStart(Marker marker) {
1376
- WritableMap event = makeClickEventData(marker.getPosition());
1377
- // todo: use Fabric events
1378
- dispatchEvent(event, OnMarkerDragStartEvent::new);
1379
-
1380
- MapMarker markerView = getMarkerMap(marker);
1381
- event = makeClickEventData(marker.getPosition());
1382
- // todo: use Fabric events
1383
- // manager.pushEvent(context, markerView, "onDragStart", event);
1384
- }
1385
-
1386
- @Override
1387
- public void onMarkerDrag(Marker marker) {
1388
- WritableMap event = makeClickEventData(marker.getPosition());
1389
- dispatchEvent(event, OnMarkerDragEvent::new);
1390
-
1391
- MapMarker markerView = getMarkerMap(marker);
1392
- event = makeClickEventData(marker.getPosition());
1393
- // todo: use Fabric events
1394
- // manager.pushEvent(context, markerView, "onDrag", event);
1395
- }
1396
-
1397
- @Override
1398
- public void onMarkerDragEnd(Marker marker) {
1399
- WritableMap event = makeClickEventData(marker.getPosition());
1400
- dispatchEvent(event, OnMarkerDragEndEvent::new);
1401
-
1402
-
1403
- MapMarker markerView = getMarkerMap(marker);
1404
- event = makeClickEventData(marker.getPosition());
1405
- // todo: use Fabric events
1406
- // manager.pushEvent(context, markerView, "onDragEnd", event);
1407
- }
1408
-
1409
- @Override
1410
- public void onPoiClick(PointOfInterest poi) {
1411
- WritableMap event = makeClickEventData(poi.latLng);
1412
-
1413
- event.putString("placeId", poi.placeId);
1414
- event.putString("name", poi.name);
1415
- dispatchEvent(event, OnPoiClickEvent::new);
1416
- }
1417
-
1418
- private ProgressBar getMapLoadingProgressBar() {
1419
- if (this.mapLoadingProgressBar == null) {
1420
- this.mapLoadingProgressBar = new ProgressBar(getContext());
1421
- this.mapLoadingProgressBar.setIndeterminate(true);
1422
- }
1423
- if (this.loadingIndicatorColor != null) {
1424
- this.setLoadingIndicatorColor(this.loadingIndicatorColor);
1425
- }
1426
- return this.mapLoadingProgressBar;
1427
- }
1428
-
1429
- private RelativeLayout getMapLoadingLayoutView() {
1430
- if (this.mapLoadingLayout == null) {
1431
- this.mapLoadingLayout = new RelativeLayout(getContext());
1432
- this.mapLoadingLayout.setBackgroundColor(Color.LTGRAY);
1433
- this.addView(this.mapLoadingLayout,
1434
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1435
- ViewGroup.LayoutParams.MATCH_PARENT));
1436
-
1437
- RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
1438
- RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
1439
- params.addRule(RelativeLayout.CENTER_IN_PARENT);
1440
- this.mapLoadingLayout.addView(this.getMapLoadingProgressBar(), params);
1441
-
1442
- this.mapLoadingLayout.setVisibility(View.INVISIBLE);
1443
- }
1444
- this.setLoadingBackgroundColor(this.loadingBackgroundColor);
1445
- return this.mapLoadingLayout;
1446
- }
1447
-
1448
- private ImageView getCacheImageView() {
1449
- if (this.cacheImageView == null) {
1450
- this.cacheImageView = new ImageView(getContext());
1451
- this.addView(this.cacheImageView,
1452
- new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1453
- ViewGroup.LayoutParams.MATCH_PARENT));
1454
- this.cacheImageView.setVisibility(View.INVISIBLE);
1455
- }
1456
- return this.cacheImageView;
1457
- }
1458
-
1459
- private void removeCacheImageView() {
1460
- if (this.cacheImageView != null) {
1461
- ((ViewGroup) this.cacheImageView.getParent()).removeView(this.cacheImageView);
1462
- this.cacheImageView = null;
1463
- }
1464
- }
1465
-
1466
- private void removeMapLoadingProgressBar() {
1467
- if (this.mapLoadingProgressBar != null) {
1468
- ((ViewGroup) this.mapLoadingProgressBar.getParent()).removeView(this.mapLoadingProgressBar);
1469
- this.mapLoadingProgressBar = null;
1470
- }
1471
- }
1472
-
1473
- private void removeMapLoadingLayoutView() {
1474
- this.removeMapLoadingProgressBar();
1475
- if (this.mapLoadingLayout != null) {
1476
- ((ViewGroup) this.mapLoadingLayout.getParent()).removeView(this.mapLoadingLayout);
1477
- this.mapLoadingLayout = null;
1478
- }
1479
- }
1480
-
1481
- private void cacheView() {
1482
- if (this.cacheEnabled) {
1483
- final ImageView cacheImageView = this.getCacheImageView();
1484
- final RelativeLayout mapLoadingLayout = this.getMapLoadingLayoutView();
1485
- cacheImageView.setVisibility(View.INVISIBLE);
1486
- mapLoadingLayout.setVisibility(View.VISIBLE);
1487
- if (this.isMapLoaded) {
1488
- this.map.snapshot(new GoogleMap.SnapshotReadyCallback() {
1489
- @Override public void onSnapshotReady(Bitmap bitmap) {
1490
- cacheImageView.setImageBitmap(bitmap);
1491
- cacheImageView.setVisibility(View.VISIBLE);
1492
- mapLoadingLayout.setVisibility(View.INVISIBLE);
1493
- }
1494
- });
1495
- }
1496
- } else {
1497
- this.removeCacheImageView();
1498
- if (this.isMapLoaded) {
1499
- this.removeMapLoadingLayoutView();
1500
- }
1501
- }
1502
- }
1503
-
1504
- public void onPanDrag(MotionEvent ev) {
1505
- Point point = new Point((int) ev.getX(), (int) ev.getY());
1506
- LatLng coords = this.map.getProjection().fromScreenLocation(point);
1507
- WritableMap event = makeClickEventData(coords);
1508
- // todo: use Fabric events
1509
- // manager.pushEvent(context, this, "onPanDrag", event);
1510
- }
1511
-
1512
- public void onDoublePress(MotionEvent ev) {
1513
- if (this.map == null) return;
1514
- Point point = new Point((int) ev.getX(), (int) ev.getY());
1515
- LatLng coords = this.map.getProjection().fromScreenLocation(point);
1516
- WritableMap event = makeClickEventData(coords);
1517
- // todo: use Fabric events
1518
- // manager.pushEvent(context, this, "onDoublePress", event);
1519
- }
1520
-
1521
- public void setKmlSrc(String kmlSrc) {
1522
- try {
1523
- InputStream kmlStream = new FileUtil(context).execute(kmlSrc).get();
1524
-
1525
- if (kmlStream == null) {
1526
- return;
1527
- }
1528
-
1529
- KmlLayer kmlLayer = new KmlLayer(map, kmlStream, context, markerManager, polygonManager, polylineManager, groundOverlayManager, null);
1530
- kmlLayer.addLayerToMap();
1531
-
1532
- WritableMap pointers = new WritableNativeMap();
1533
- WritableArray markers = new WritableNativeArray();
1534
-
1535
- if (kmlLayer.getContainers() == null) {
1453
+ MapMarker markerView = getMarkerMap(marker);
1454
+ event = makeClickEventData(marker.getPosition());
1536
1455
  // todo: use Fabric events
1537
- // manager.pushEvent(context, this, "onKmlReady", pointers);
1538
- return;
1539
- }
1456
+ // manager.pushEvent(context, markerView, "onDrag", event);
1457
+ }
1540
1458
 
1541
- //Retrieve a nested container within the first container
1542
- KmlContainer container = kmlLayer.getContainers().iterator().next();
1543
- if (container == null || container.getContainers() == null) {
1459
+ @Override
1460
+ public void onMarkerDragEnd(Marker marker) {
1461
+ WritableMap event = makeClickEventData(marker.getPosition());
1462
+ dispatchEvent(event, OnMarkerDragEndEvent::new);
1463
+
1464
+
1465
+ MapMarker markerView = getMarkerMap(marker);
1466
+ event = makeClickEventData(marker.getPosition());
1544
1467
  // todo: use Fabric events
1545
- // manager.pushEvent(context, this, "onKmlReady", pointers);
1546
- return;
1547
- }
1468
+ // manager.pushEvent(context, markerView, "onDragEnd", event);
1469
+ }
1470
+
1471
+ @Override
1472
+ public void onPoiClick(PointOfInterest poi) {
1473
+ WritableMap event = makeClickEventData(poi.latLng);
1474
+
1475
+ event.putString("placeId", poi.placeId);
1476
+ event.putString("name", poi.name);
1477
+ dispatchEvent(event, OnPoiClickEvent::new);
1478
+ }
1548
1479
 
1480
+ private ProgressBar getMapLoadingProgressBar() {
1481
+ if (this.mapLoadingProgressBar == null) {
1482
+ this.mapLoadingProgressBar = new ProgressBar(getContext());
1483
+ this.mapLoadingProgressBar.setIndeterminate(true);
1484
+ }
1485
+ if (this.loadingIndicatorColor != null) {
1486
+ this.setLoadingIndicatorColor(this.loadingIndicatorColor);
1487
+ }
1488
+ return this.mapLoadingProgressBar;
1489
+ }
1549
1490
 
1550
- if (container.getContainers().iterator().hasNext()) {
1551
- container = container.getContainers().iterator().next();
1552
- }
1491
+ private RelativeLayout getMapLoadingLayoutView() {
1492
+ if (this.mapLoadingLayout == null) {
1493
+ this.mapLoadingLayout = new RelativeLayout(getContext());
1494
+ this.mapLoadingLayout.setBackgroundColor(Color.LTGRAY);
1495
+ this.addView(this.mapLoadingLayout,
1496
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1497
+ ViewGroup.LayoutParams.MATCH_PARENT));
1553
1498
 
1554
- int index = 0;
1555
- for (KmlPlacemark placemark : container.getPlacemarks()) {
1556
- MarkerOptions options = new MarkerOptions();
1499
+ RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
1500
+ RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
1501
+ params.addRule(RelativeLayout.CENTER_IN_PARENT);
1502
+ this.mapLoadingLayout.addView(this.getMapLoadingProgressBar(), params);
1557
1503
 
1558
- if (placemark.getInlineStyle() != null) {
1559
- options = placemark.getMarkerOptions();
1560
- } else {
1561
- options.icon(BitmapDescriptorFactory.defaultMarker());
1504
+ this.mapLoadingLayout.setVisibility(View.INVISIBLE);
1562
1505
  }
1506
+ this.setLoadingBackgroundColor(this.loadingBackgroundColor);
1507
+ return this.mapLoadingLayout;
1508
+ }
1563
1509
 
1564
- LatLng latLng = ((LatLng) placemark.getGeometry().getGeometryObject());
1565
- String title = "";
1566
- String snippet = "";
1510
+ private ImageView getCacheImageView() {
1511
+ if (this.cacheImageView == null) {
1512
+ this.cacheImageView = new ImageView(getContext());
1513
+ this.addView(this.cacheImageView,
1514
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
1515
+ ViewGroup.LayoutParams.MATCH_PARENT));
1516
+ this.cacheImageView.setVisibility(View.INVISIBLE);
1517
+ }
1518
+ return this.cacheImageView;
1519
+ }
1520
+
1521
+ private void removeCacheImageView() {
1522
+ if (this.cacheImageView != null) {
1523
+ ((ViewGroup) this.cacheImageView.getParent()).removeView(this.cacheImageView);
1524
+ this.cacheImageView = null;
1525
+ }
1526
+ }
1567
1527
 
1568
- if (placemark.hasProperty("name")) {
1569
- title = placemark.getProperty("name");
1528
+ private void removeMapLoadingProgressBar() {
1529
+ if (this.mapLoadingProgressBar != null) {
1530
+ ((ViewGroup) this.mapLoadingProgressBar.getParent()).removeView(this.mapLoadingProgressBar);
1531
+ this.mapLoadingProgressBar = null;
1570
1532
  }
1533
+ }
1571
1534
 
1572
- if (placemark.hasProperty("description")) {
1573
- snippet = placemark.getProperty("description");
1535
+ private void removeMapLoadingLayoutView() {
1536
+ this.removeMapLoadingProgressBar();
1537
+ if (this.mapLoadingLayout != null) {
1538
+ ((ViewGroup) this.mapLoadingLayout.getParent()).removeView(this.mapLoadingLayout);
1539
+ this.mapLoadingLayout = null;
1574
1540
  }
1541
+ }
1542
+
1543
+ private void cacheView() {
1544
+ if (this.cacheEnabled) {
1545
+ final ImageView cacheImageView = this.getCacheImageView();
1546
+ final RelativeLayout mapLoadingLayout = this.getMapLoadingLayoutView();
1547
+ cacheImageView.setVisibility(View.INVISIBLE);
1548
+ mapLoadingLayout.setVisibility(View.VISIBLE);
1549
+ if (this.isMapLoaded) {
1550
+ this.map.snapshot(new GoogleMap.SnapshotReadyCallback() {
1551
+ @Override
1552
+ public void onSnapshotReady(Bitmap bitmap) {
1553
+ cacheImageView.setImageBitmap(bitmap);
1554
+ cacheImageView.setVisibility(View.VISIBLE);
1555
+ mapLoadingLayout.setVisibility(View.INVISIBLE);
1556
+ }
1557
+ });
1558
+ }
1559
+ } else {
1560
+ this.removeCacheImageView();
1561
+ if (this.isMapLoaded) {
1562
+ this.removeMapLoadingLayoutView();
1563
+ }
1564
+ }
1565
+ }
1566
+
1567
+ public void onPanDrag(MotionEvent ev) {
1568
+ Point point = new Point((int) ev.getX(), (int) ev.getY());
1569
+ LatLng coords = this.map.getProjection().fromScreenLocation(point);
1570
+ WritableMap event = makeClickEventData(coords);
1571
+ // todo: use Fabric events
1572
+ // manager.pushEvent(context, this, "onPanDrag", event);
1573
+ }
1574
+
1575
+ public void onDoublePress(MotionEvent ev) {
1576
+ if (this.map == null) return;
1577
+ Point point = new Point((int) ev.getX(), (int) ev.getY());
1578
+ LatLng coords = this.map.getProjection().fromScreenLocation(point);
1579
+ WritableMap event = makeClickEventData(coords);
1580
+ // todo: use Fabric events
1581
+ // manager.pushEvent(context, this, "onDoublePress", event);
1582
+ }
1583
+
1584
+ public void setKmlSrc(String kmlSrc) {
1585
+ try {
1586
+ InputStream kmlStream = new FileUtil(context).execute(kmlSrc).get();
1587
+
1588
+ if (kmlStream == null) {
1589
+ return;
1590
+ }
1591
+
1592
+ KmlLayer kmlLayer = new KmlLayer(map, kmlStream, context, markerManager, polygonManager, polylineManager, groundOverlayManager, null);
1593
+ kmlLayer.addLayerToMap();
1594
+
1595
+ WritableMap pointers = new WritableNativeMap();
1596
+ WritableArray markers = new WritableNativeArray();
1597
+
1598
+ if (kmlLayer.getContainers() == null) {
1599
+ // todo: use Fabric events
1600
+ // manager.pushEvent(context, this, "onKmlReady", pointers);
1601
+ return;
1602
+ }
1603
+
1604
+ //Retrieve a nested container within the first container
1605
+ KmlContainer container = kmlLayer.getContainers().iterator().next();
1606
+ if (container == null || container.getContainers() == null) {
1607
+ // todo: use Fabric events
1608
+ // manager.pushEvent(context, this, "onKmlReady", pointers);
1609
+ return;
1610
+ }
1611
+
1575
1612
 
1576
- options.position(latLng);
1577
- options.title(title);
1578
- options.snippet(snippet);
1579
- // FIXME: get markerManager from somewhere
1613
+ if (container.getContainers().iterator().hasNext()) {
1614
+ container = container.getContainers().iterator().next();
1615
+ }
1616
+
1617
+ int index = 0;
1618
+ for (KmlPlacemark placemark : container.getPlacemarks()) {
1619
+ MarkerOptions options = new MarkerOptions();
1620
+
1621
+ if (placemark.getInlineStyle() != null) {
1622
+ options = placemark.getMarkerOptions();
1623
+ } else {
1624
+ options.icon(BitmapDescriptorFactory.defaultMarker());
1625
+ }
1626
+
1627
+ LatLng latLng = ((LatLng) placemark.getGeometry().getGeometryObject());
1628
+ String title = "";
1629
+ String snippet = "";
1630
+
1631
+ if (placemark.hasProperty("name")) {
1632
+ title = placemark.getProperty("name");
1633
+ }
1634
+
1635
+ if (placemark.hasProperty("description")) {
1636
+ snippet = placemark.getProperty("description");
1637
+ }
1638
+
1639
+ options.position(latLng);
1640
+ options.title(title);
1641
+ options.snippet(snippet);
1642
+ // FIXME: get markerManager from somewhere
1580
1643
  /*
1581
1644
  MapMarker marker = new MapMarker(context, options, this.manager.getMarkerManager());
1582
1645
 
@@ -1602,121 +1665,122 @@ public static CameraPosition cameraPositionFromMap(ReadableMap camera){
1602
1665
 
1603
1666
  markers.pushMap(loadedMarker);
1604
1667
  */
1605
- }
1606
-
1607
- pointers.putArray("markers", markers);
1608
- // todo: use Fabric events
1609
- // manager.pushEvent(context, this, "onKmlReady", pointers);
1610
-
1611
- } catch (XmlPullParserException | IOException | InterruptedException | ExecutionException e) {
1612
- e.printStackTrace();
1613
- }
1614
- }
1615
-
1616
- @Override
1617
- public void onIndoorBuildingFocused() {
1618
- IndoorBuilding building = this.map.getFocusedBuilding();
1619
- if (building != null) {
1620
- List<IndoorLevel> levels = building.getLevels();
1621
- int index = 0;
1622
- WritableArray levelsArray = Arguments.createArray();
1623
- for (IndoorLevel level : levels) {
1624
- WritableMap levelMap = Arguments.createMap();
1625
- levelMap.putInt("index", index);
1626
- levelMap.putString("name", level.getName());
1627
- levelMap.putString("shortName", level.getShortName());
1628
- levelsArray.pushMap(levelMap);
1629
- index++;
1630
- }
1631
- WritableMap event = Arguments.createMap();
1632
- WritableMap indoorBuilding = Arguments.createMap();
1633
- indoorBuilding.putArray("levels", levelsArray);
1634
- indoorBuilding.putInt("activeLevelIndex", building.getActiveLevelIndex());
1635
- indoorBuilding.putBoolean("underground", building.isUnderground());
1636
-
1637
- event.putMap("IndoorBuilding", indoorBuilding);
1638
- // todo: use Fabric events
1639
- // manager.pushEvent(context, this, "onIndoorBuildingFocused", event);
1640
- } else {
1641
- WritableMap event = Arguments.createMap();
1642
- WritableArray levelsArray = Arguments.createArray();
1643
- WritableMap indoorBuilding = Arguments.createMap();
1644
- indoorBuilding.putArray("levels", levelsArray);
1645
- indoorBuilding.putInt("activeLevelIndex", 0);
1646
- indoorBuilding.putBoolean("underground", false);
1647
-
1648
- event.putMap("IndoorBuilding", indoorBuilding);
1649
- // todo: use Fabric events
1650
- // manager.pushEvent(context, this, "onIndoorBuildingFocused", event);
1651
- }
1652
- }
1653
-
1654
- @Override
1655
- public void onIndoorLevelActivated(IndoorBuilding building) {
1656
- if (building == null) {
1657
- return;
1658
- }
1659
- int activeLevelIndex = building.getActiveLevelIndex();
1660
- if (activeLevelIndex < 0 || activeLevelIndex >= building.getLevels().size()) {
1661
- return;
1662
- }
1663
- IndoorLevel level = building.getLevels().get(activeLevelIndex);
1664
-
1665
- WritableMap event = Arguments.createMap();
1666
- WritableMap indoorlevel = Arguments.createMap();
1667
-
1668
- indoorlevel.putInt("activeLevelIndex", activeLevelIndex);
1669
- indoorlevel.putString("name", level.getName());
1670
- indoorlevel.putString("shortName", level.getShortName());
1671
-
1672
- event.putMap("IndoorLevel", indoorlevel);
1673
- // todo: use Fabric events
1674
- // manager.pushEvent(context, this, "onIndoorLevelActivated", event);
1675
- }
1676
-
1677
- public void setIndoorActiveLevelIndex(int activeLevelIndex) {
1678
- IndoorBuilding building = this.map.getFocusedBuilding();
1679
- if (building != null) {
1680
- if (activeLevelIndex >= 0 && activeLevelIndex < building.getLevels().size()) {
1681
- IndoorLevel level = building.getLevels().get(activeLevelIndex);
1682
- if (level != null) {
1683
- level.activate();
1668
+ }
1669
+
1670
+ pointers.putArray("markers", markers);
1671
+ // todo: use Fabric events
1672
+ // manager.pushEvent(context, this, "onKmlReady", pointers);
1673
+
1674
+ } catch (XmlPullParserException | IOException | InterruptedException |
1675
+ ExecutionException e) {
1676
+ e.printStackTrace();
1677
+ }
1678
+ }
1679
+
1680
+ @Override
1681
+ public void onIndoorBuildingFocused() {
1682
+ IndoorBuilding building = this.map.getFocusedBuilding();
1683
+ if (building != null) {
1684
+ List<IndoorLevel> levels = building.getLevels();
1685
+ int index = 0;
1686
+ WritableArray levelsArray = Arguments.createArray();
1687
+ for (IndoorLevel level : levels) {
1688
+ WritableMap levelMap = Arguments.createMap();
1689
+ levelMap.putInt("index", index);
1690
+ levelMap.putString("name", level.getName());
1691
+ levelMap.putString("shortName", level.getShortName());
1692
+ levelsArray.pushMap(levelMap);
1693
+ index++;
1694
+ }
1695
+ WritableMap event = Arguments.createMap();
1696
+ WritableMap indoorBuilding = Arguments.createMap();
1697
+ indoorBuilding.putArray("levels", levelsArray);
1698
+ indoorBuilding.putInt("activeLevelIndex", building.getActiveLevelIndex());
1699
+ indoorBuilding.putBoolean("underground", building.isUnderground());
1700
+
1701
+ event.putMap("IndoorBuilding", indoorBuilding);
1702
+ // todo: use Fabric events
1703
+ // manager.pushEvent(context, this, "onIndoorBuildingFocused", event);
1704
+ } else {
1705
+ WritableMap event = Arguments.createMap();
1706
+ WritableArray levelsArray = Arguments.createArray();
1707
+ WritableMap indoorBuilding = Arguments.createMap();
1708
+ indoorBuilding.putArray("levels", levelsArray);
1709
+ indoorBuilding.putInt("activeLevelIndex", 0);
1710
+ indoorBuilding.putBoolean("underground", false);
1711
+
1712
+ event.putMap("IndoorBuilding", indoorBuilding);
1713
+ // todo: use Fabric events
1714
+ // manager.pushEvent(context, this, "onIndoorBuildingFocused", event);
1684
1715
  }
1685
- }
1686
1716
  }
1687
- }
1688
1717
 
1689
- private MapMarker getMarkerMap(Marker marker) {
1690
- MapMarker airMarker = markerMap.get(marker);
1718
+ @Override
1719
+ public void onIndoorLevelActivated(IndoorBuilding building) {
1720
+ if (building == null) {
1721
+ return;
1722
+ }
1723
+ int activeLevelIndex = building.getActiveLevelIndex();
1724
+ if (activeLevelIndex < 0 || activeLevelIndex >= building.getLevels().size()) {
1725
+ return;
1726
+ }
1727
+ IndoorLevel level = building.getLevels().get(activeLevelIndex);
1728
+
1729
+ WritableMap event = Arguments.createMap();
1730
+ WritableMap indoorlevel = Arguments.createMap();
1731
+
1732
+ indoorlevel.putInt("activeLevelIndex", activeLevelIndex);
1733
+ indoorlevel.putString("name", level.getName());
1734
+ indoorlevel.putString("shortName", level.getShortName());
1691
1735
 
1692
- if (airMarker != null) {
1693
- return airMarker;
1736
+ event.putMap("IndoorLevel", indoorlevel);
1737
+ // todo: use Fabric events
1738
+ // manager.pushEvent(context, this, "onIndoorLevelActivated", event);
1694
1739
  }
1695
1740
 
1696
- for (Map.Entry<Marker, MapMarker> entryMarker : markerMap.entrySet()) {
1697
- if (entryMarker.getKey().getPosition().equals(marker.getPosition())
1698
- && entryMarker.getKey().getTitle().equals(marker.getTitle())) {
1699
- airMarker = entryMarker.getValue();
1700
- break;
1701
- }
1741
+ public void setIndoorActiveLevelIndex(int activeLevelIndex) {
1742
+ IndoorBuilding building = this.map.getFocusedBuilding();
1743
+ if (building != null) {
1744
+ if (activeLevelIndex >= 0 && activeLevelIndex < building.getLevels().size()) {
1745
+ IndoorLevel level = building.getLevels().get(activeLevelIndex);
1746
+ if (level != null) {
1747
+ level.activate();
1748
+ }
1749
+ }
1750
+ }
1702
1751
  }
1703
1752
 
1704
- return airMarker;
1705
- }
1753
+ private MapMarker getMarkerMap(Marker marker) {
1754
+ MapMarker airMarker = markerMap.get(marker);
1706
1755
 
1707
- @Override
1708
- public void requestLayout() {
1709
- super.requestLayout();
1710
- post(measureAndLayout);
1711
- }
1756
+ if (airMarker != null) {
1757
+ return airMarker;
1758
+ }
1759
+
1760
+ for (Map.Entry<Marker, MapMarker> entryMarker : markerMap.entrySet()) {
1761
+ if (entryMarker.getKey().getPosition().equals(marker.getPosition())
1762
+ && entryMarker.getKey().getTitle().equals(marker.getTitle())) {
1763
+ airMarker = entryMarker.getValue();
1764
+ break;
1765
+ }
1766
+ }
1767
+
1768
+ return airMarker;
1769
+ }
1712
1770
 
1713
- private final Runnable measureAndLayout = new Runnable() {
1714
1771
  @Override
1715
- public void run() {
1716
- measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
1717
- MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
1718
- layout(getLeft(), getTop(), getRight(), getBottom());
1772
+ public void requestLayout() {
1773
+ super.requestLayout();
1774
+ post(measureAndLayout);
1719
1775
  }
1720
- };
1776
+
1777
+ private final Runnable measureAndLayout = new Runnable() {
1778
+ @Override
1779
+ public void run() {
1780
+ measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
1781
+ MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
1782
+ layout(getLeft(), getTop(), getRight(), getBottom());
1783
+ }
1784
+ };
1721
1785
 
1722
1786
  }