diff --git a/android/build.gradle b/android/build.gradle index 9017c88b..9ef4b16c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,11 +1,11 @@ dependencies { // https://developers.google.com/android/guides/releases - implementation 'com.google.android.gms:play-services-maps:19.2.0' + implementation 'com.google.android.gms:play-services-maps:20.0.0' // https://github.com/googlemaps/android-maps-utils/releases - implementation 'com.google.maps.android:android-maps-utils:3.14.0' + implementation 'com.google.maps.android:android-maps-utils:3.16.0' // https://developer.android.com/jetpack/androidx/releases/fragment - implementation 'androidx.fragment:fragment:1.7.1' + implementation 'androidx.fragment:fragment:1.8.9' } diff --git a/android/manifest b/android/manifest index 41316e6a..93033be0 100644 --- a/android/manifest +++ b/android/manifest @@ -2,7 +2,7 @@ # this is your module manifest and used by Titanium # during compilation, packaging, distribution, etc. # -version: 5.7.0 +version: 5.8.0 apiversion: 4 architectures: arm64-v8a armeabi-v7a x86 x86_64 description: External version of Map module using native Google Maps library diff --git a/android/src/ti/map/AnnotationProxy.java b/android/src/ti/map/AnnotationProxy.java index 45e067c9..d589baf2 100644 --- a/android/src/ti/map/AnnotationProxy.java +++ b/android/src/ti/map/AnnotationProxy.java @@ -85,24 +85,15 @@ public AnnotationProxy() @Override public void release() { - if (hasProperty(MapModule.PROPERTY_CUSTOM_VIEW)) { - TiViewProxy customView = (TiViewProxy) getProperty(MapModule.PROPERTY_CUSTOM_VIEW); - customView.release(); - } - /* - if (markerOptions != null) { - markerOptions = null; - } - if (clusterMarker != null) { - clusterMarker = null; + Object customViewProperty = getProperty(MapModule.PROPERTY_CUSTOM_VIEW); + if (customViewProperty instanceof TiViewProxy) { + ((TiViewProxy) customViewProperty).release(); } if (infoWindow != null) { + infoWindow.removeAllViews(); infoWindow = null; } - if (delegate != null) { - delegate = null; - } - */ + delegate = null; super.release(); } @@ -159,9 +150,9 @@ public boolean handleMessage(Message msg) case MSG_SET_HIDDEN: { result = (AsyncResult) msg.obj; - Marker m = marker.getMarker(); - if (m != null) { - m.setVisible(!(Boolean) result.getArg()); + Marker hiddenMarker = getNativeMarker(); + if (hiddenMarker != null) { + hiddenMarker.setVisible(!(Boolean) result.getArg()); } result.setResult(null); return true; @@ -169,9 +160,9 @@ public boolean handleMessage(Message msg) case MSG_SET_DRAGGABLE: { result = (AsyncResult) msg.obj; - Marker m = marker.getMarker(); - if (m != null) { - m.setDraggable((Boolean) result.getArg()); + Marker draggableMarker = getNativeMarker(); + if (draggableMarker != null) { + draggableMarker.setDraggable((Boolean) result.getArg()); } result.setResult(null); return true; @@ -182,14 +173,12 @@ public boolean handleMessage(Message msg) return true; } - case MSG_SET_IMAGE: + case MSG_SET_IMAGE: { result = (AsyncResult) msg.obj; - Marker m = marker.getMarker(); - if (m != null) { - updateImage(m, result.getArg()); - } + updateImage(getNativeMarker(), result.getArg()); result.setResult(null); return true; + } default: { return super.handleMessage(msg); @@ -197,9 +186,18 @@ public boolean handleMessage(Message msg) } } + /** + * The native marker, or null while the annotation is not (yet) rendered on the map. + * Clustered annotations only get one once TiClusterRenderer has rendered them. + */ + private Marker getNativeMarker() + { + return (marker != null) ? marker.getMarker() : null; + } + public void setPosition(double latitude, double longitude) { - Marker m = marker.getMarker(); + Marker m = getNativeMarker(); if (m != null) { animateMarkerToPosition(m, new LatLng(latitude, longitude)); } @@ -315,32 +313,20 @@ private void handleCustomView(Object obj) private void handleImage(Object image) { - // Image path + Bitmap bitmap = null; if (image instanceof String) { - TiDrawableReference imageref = TiDrawableReference.fromUrl(this, (String) image); - Bitmap bitmap = imageref.getBitmap(); - if (bitmap != null) { - try { - markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)); - setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - } - return; - } + bitmap = TiDrawableReference.fromUrl(this, (String) image).getBitmap(); + } else if (image instanceof TiBlob) { + bitmap = ((TiBlob) image).getImage(); } - // Image blob - if (image instanceof TiBlob) { - Bitmap bitmap = ((TiBlob) image).getImage(); - if (bitmap != null) { - try { - markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)); - setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); - } catch (Exception e) { - Log.e(TAG, e.getMessage()); - } + if (bitmap != null) { + try { + markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)); + setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); return; + } catch (Exception e) { + Log.e(TAG, "Unable to apply the marker image", e); } } @@ -355,46 +341,41 @@ public MarkerOptions getMarkerOptions() public void updateImage(Marker m, Object value) { + if (m == null) { + return; + } if (hasProperty(MapModule.PROPERTY_CUSTOM_VIEW)) { // Custom view used. Update image not allowed return; } + // Clearing the image falls back to the pin, tinted with "pinColor" when one is set. if (value == null) { - m.setIcon(BitmapDescriptorFactory.defaultMarker(TiConvert.toFloat(getProperty(TiC.PROPERTY_PINCOLOR)))); + Object pinColor = getProperty(TiC.PROPERTY_PINCOLOR); + m.setIcon(pinColor != null ? BitmapDescriptorFactory.defaultMarker(TiConvert.toFloat(pinColor)) + : BitmapDescriptorFactory.defaultMarker()); setIconImageDimensions(-1, -1); return; } // image not null has only effect if customView is null. Any other case, customView has more priority - if (value != null) { - // Image path - if (value instanceof String) { - TiDrawableReference imageref = TiDrawableReference.fromUrl(this, (String) value); - Bitmap bitmap = imageref.getBitmap(); - if (bitmap != null) { - try { - if (m != null) { - m.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap)); - setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); - } - } catch (Exception e) { - } - return; - } - } + Bitmap bitmap = null; + if (value instanceof String) { + bitmap = TiDrawableReference.fromUrl(this, (String) value).getBitmap(); + } else if (value instanceof TiBlob) { + bitmap = ((TiBlob) value).getImage(); + } - // Image blob - if (value instanceof TiBlob) { - Bitmap bitmap = ((TiBlob) value).getImage(); - if (bitmap != null) { - if (m != null) { - m.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap)); - setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); - } - return; - } - } + if (bitmap == null) { + Log.w(TAG, "Unable to get the image from: " + value); + return; + } + + try { + m.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap)); + setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); + } catch (Exception e) { + Log.e(TAG, "Unable to apply the marker image", e); } } diff --git a/android/src/ti/map/CameraProxy.java b/android/src/ti/map/CameraProxy.java index 5787140c..f3c2de43 100644 --- a/android/src/ti/map/CameraProxy.java +++ b/android/src/ti/map/CameraProxy.java @@ -83,6 +83,11 @@ public void onPropertyChanged(String name, Object value) private void updateCamera() { + // CameraPosition requires a target. Without "centerCoordinates" there is nothing to move the camera to. + if (position == null) { + cameraPosition = null; + return; + } cameraPosition = new CameraPosition.Builder().target(position).zoom(zoom).bearing(heading).tilt(pitch).build(); } @@ -94,4 +99,10 @@ public CameraUpdate getCamera() return null; } } + + @Override + public String getApiName() + { + return "Ti.Map.Camera"; + } } diff --git a/android/src/ti/map/ImageOverlayProxy.java b/android/src/ti/map/ImageOverlayProxy.java index f98c5871..6576ca97 100644 --- a/android/src/ti/map/ImageOverlayProxy.java +++ b/android/src/ti/map/ImageOverlayProxy.java @@ -7,7 +7,7 @@ package ti.map; -import com.google.android.gms.maps.model.BitmapDescriptor; +import android.graphics.Bitmap; import com.google.android.gms.maps.model.BitmapDescriptorFactory; import com.google.android.gms.maps.model.GroundOverlay; import com.google.android.gms.maps.model.GroundOverlayOptions; @@ -16,6 +16,7 @@ import org.appcelerator.kroll.KrollDict; import org.appcelerator.kroll.KrollProxy; import org.appcelerator.kroll.annotations.Kroll; +import org.appcelerator.kroll.common.Log; import org.appcelerator.titanium.view.TiDrawableReference; @Kroll.proxy(creatableInModule = MapModule.class) @@ -73,10 +74,15 @@ private void handleBoundsCoordinate(KrollDict boundsCoordinateDict) private void handleImage(Object image) { TiDrawableReference source = TiDrawableReference.fromObject(this, image); - if (!source.isTypeNull()) { - BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(source.getBitmap()); - groundOverlayOptions.image(bitmapDescriptor); + if (source.isTypeNull()) { + return; } + Bitmap bitmap = source.getBitmap(); + if (bitmap == null) { + Log.w(TAG, "Unable to load the image overlay image: " + image); + return; + } + groundOverlayOptions.image(BitmapDescriptorFactory.fromBitmap(bitmap)); } public GroundOverlayOptions getGroundOverlayOptions() diff --git a/android/src/ti/map/PolylineProxy.java b/android/src/ti/map/PolylineProxy.java index 097fd3c3..f0a3cc22 100644 --- a/android/src/ti/map/PolylineProxy.java +++ b/android/src/ti/map/PolylineProxy.java @@ -290,8 +290,6 @@ else if (name.equals(TiC.PROPERTY_TOUCH_ENABLED)) { private List processPatternDefinition(HashMap definition) { List pattern = null; - int type = (Integer) definition.get("type"); - int gapLength = (Integer) definition.get("gapLength"); if (definition.get("type") == null) { Log.e(TAG, "No pattern type specified. Using default dashed pattern."); @@ -299,8 +297,11 @@ private List processPatternDefinition(HashMap definition) return DEFAULT_DASHED_PATTERN; } + int type = TiConvert.toInt(definition.get("type")); + int gapLength = TiConvert.toInt(definition.get("gapLength"), DEFAULT_PATTERN_GAP_LENGTH_PX); + if (type == MapModule.POLYLINE_PATTERN_DASHED) { - int dashLength = (Integer) definition.get("dashLength"); + int dashLength = TiConvert.toInt(definition.get("dashLength"), DEFAULT_PATTERN_DASH_LENGTH_PX); pattern = Arrays.asList(new Dash(dashLength), new Gap(gapLength)); } else if (type == MapModule.POLYLINE_PATTERN_DOTTED) { diff --git a/android/src/ti/map/Shape/PolylineBoundary.java b/android/src/ti/map/Shape/PolylineBoundary.java index b092e21a..84eaaae1 100644 --- a/android/src/ti/map/Shape/PolylineBoundary.java +++ b/android/src/ti/map/Shape/PolylineBoundary.java @@ -1,7 +1,6 @@ package ti.map.Shape; import android.graphics.PointF; -import android.util.Log; import com.google.android.gms.maps.model.LatLng; import java.util.ArrayList; import ti.map.PolylineProxy; @@ -76,7 +75,7 @@ public boolean contains(PointF[] points, PointF test) { double diffX = point2.x - point1.x; - double diffY = point2.y - point.y; + double diffY = point2.y - point1.y; if ((diffX == 0) && (diffY == 0)) { diffX = test.x - point1.x; diffY = test.y - point1.y; diff --git a/android/src/ti/map/TiClusterRenderer.java b/android/src/ti/map/TiClusterRenderer.java index 5065e59f..caa0ede5 100644 --- a/android/src/ti/map/TiClusterRenderer.java +++ b/android/src/ti/map/TiClusterRenderer.java @@ -39,6 +39,9 @@ protected void onBeforeClusterItemRendered(TiMarker clusterItem, MarkerOptions m if (anno != null) { if (anno.hasProperty(TiC.PROPERTY_IMAGE)) { handleImage(anno, markerOptions, anno.getProperty(TiC.PROPERTY_IMAGE)); + } else { + // Fall back to the default icon size so "centerOffset" below does not divide by zero. + setIconImageDimensions(-1, -1); } if (anno.hasProperty(MapModule.PROPERTY_CENTER_OFFSET)) { @@ -53,28 +56,20 @@ protected void onBeforeClusterItemRendered(TiMarker clusterItem, MarkerOptions m private void handleImage(AnnotationProxy anno, MarkerOptions markerOptions, Object image) { - // Image path + Bitmap bitmap = null; if (image instanceof String) { - TiDrawableReference imageref = - TiDrawableReference.fromUrl(anno, (String) anno.getProperty(TiC.PROPERTY_IMAGE)); - Bitmap bitmap = imageref.getBitmap(); - if (bitmap != null) { - try { - markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)); - setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); - } catch (Exception e) { - } - return; - } + bitmap = TiDrawableReference.fromUrl(anno, (String) image).getBitmap(); + } else if (image instanceof TiBlob) { + bitmap = ((TiBlob) image).getImage(); } - // Image blob - if (image instanceof TiBlob) { - Bitmap bitmap = ((TiBlob) image).getImage(); - if (bitmap != null) { + if (bitmap != null) { + try { markerOptions.icon(BitmapDescriptorFactory.fromBitmap(bitmap)); setIconImageDimensions(bitmap.getWidth(), bitmap.getHeight()); return; + } catch (Exception e) { + Log.e(TAG, "Unable to apply the cluster item image", e); } } @@ -89,6 +84,7 @@ protected void onClusterItemRendered(TiMarker clusterItem, Marker marker) clusterItem.setMarker(marker); } + @Override protected void onClusterItemUpdated(TiMarker item, Marker marker) { boolean changed = false; @@ -110,10 +106,8 @@ protected void onClusterItemUpdated(TiMarker item, Marker marker) changed = true; } // Update marker position if the item changed position - if (!marker.getPosition().equals(item.getPosition())) { - if (item.getPosition() != null) { - marker.setPosition(item.getPosition()); - } + if (item.getPosition() != null && !item.getPosition().equals(marker.getPosition())) { + marker.setPosition(item.getPosition()); changed = true; } if (changed && marker.isInfoWindowShown()) { diff --git a/android/src/ti/map/TiMapInfoWindow.java b/android/src/ti/map/TiMapInfoWindow.java index 40fc77de..0b836b6d 100644 --- a/android/src/ti/map/TiMapInfoWindow.java +++ b/android/src/ti/map/TiMapInfoWindow.java @@ -216,11 +216,16 @@ public void analyzeTouchEvent(MotionEvent ev, Point markerPoint, int iconImageHe if (evX > markerPoint.x - infoWindowHalfWidth && evX < markerPoint.x + infoWindowHalfWidth && evY > markerPoint.y - infoWindowHeight - iconImageHeight && evY < markerPoint.y - iconImageHeight) { MotionEvent evCopy = MotionEvent.obtain(ev); - evCopy.offsetLocation(-markerPoint.x + infoWindowHalfWidth, - -markerPoint.y + infoWindowHeight + iconImageHeight); - - int x = (int) evCopy.getX(); - int y = (int) evCopy.getY(); + int x; + int y; + try { + evCopy.offsetLocation(-markerPoint.x + infoWindowHalfWidth, + -markerPoint.y + infoWindowHeight + iconImageHeight); + x = (int) evCopy.getX(); + y = (int) evCopy.getY(); + } finally { + evCopy.recycle(); + } Rect hitRect = new Rect(); diff --git a/android/src/ti/map/TiUIMapView.java b/android/src/ti/map/TiUIMapView.java index f52b68c5..ff91cceb 100644 --- a/android/src/ti/map/TiUIMapView.java +++ b/android/src/ti/map/TiUIMapView.java @@ -16,7 +16,6 @@ import android.graphics.Color; import android.graphics.Point; import android.location.Location; -import android.os.Build; import android.view.MotionEvent; import android.view.SurfaceView; import android.view.View; @@ -85,7 +84,7 @@ public class TiUIMapView extends TiUIFragment private ArrayList currentPolylines; private ArrayList currentImageOverlays; private ClusterManager mClusterManager; - private DefaultClusterRenderer clusterRender; + private DefaultClusterRenderer clusterRender; private MarkerManager mMarkerManager; private MarkerManager.Collection collection; @@ -379,7 +378,12 @@ protected void setStyle(String style) try { // Handle .json files if (style.endsWith(".json")) { - Object json = new JSONTokener(loadJSONFromAsset(style)).nextValue(); + String contents = loadJSONFromAsset(style); + if (contents == null) { + Log.e(TAG, "Cannot read JSON style file: " + style); + return; + } + Object json = new JSONTokener(contents).nextValue(); if (json instanceof JSONObject) { style = ((JSONObject) json).toString(); @@ -406,13 +410,24 @@ protected void setStyle(String style) protected void setUserLocationEnabled(boolean enabled) { + if (map == null) { + return; + } + + // Disabling never needs a permission. + if (!enabled) { + map.setMyLocationEnabled(false); + return; + } + Context context = TiApplication.getInstance().getApplicationContext(); + boolean hasLocationPermission = + context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED + || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED; - if (map != null - && (Build.VERSION.SDK_INT < 23 - || context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED)) { - map.setMyLocationEnabled(enabled); + if (hasLocationPermission) { + map.setMyLocationEnabled(true); } else { Log.e(TAG, "Enable ACCESS_FINE_LOCATION permission to use userLocation"); } @@ -485,10 +500,14 @@ protected void setPadding(int left, int top, int right, int bottom) protected void showAnnotations(Object[] annotations) { + if (map == null) { + return; + } + ArrayList markers = new ArrayList(); // Use supplied annotations first. If none available, select all (parity with iOS) - if (annotations != null) { + if (annotations != null && annotations.length > 0) { for (int i = 0; i < annotations.length; i++) { Object annotation = annotations[i]; if (annotation instanceof AnnotationProxy) { @@ -500,13 +519,22 @@ protected void showAnnotations(Object[] annotations) } LatLngBounds.Builder builder = new LatLngBounds.Builder(); + boolean hasPosition = false; for (TiMarker marker : markers) { + if (marker == null || marker.getPosition() == null) { + continue; + } builder.include(marker.getPosition()); + hasPosition = true; + } + // LatLngBounds.Builder.build() throws when no point was included. + if (!hasPosition) { + Log.w(TAG, "showAnnotations: no annotation with a valid position, nothing to show."); + return; } - LatLngBounds bounds = builder.build(); int padding = 30; - CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(bounds, padding); + CameraUpdate cu = CameraUpdateFactory.newLatLngBounds(builder.build(), padding); map.animateCamera(cu); } @@ -635,24 +663,22 @@ protected void addAnnotation(AnnotationProxy annotation) } } - if (map != null) { - annotation.processOptions(); - if (annotation.getProperty(MapModule.PROPERTY_CLUSTER_IDENTIFIER) == null) { - Marker marker = collection.addMarker(annotation.getMarkerOptions()); - tiMarker = new TiMarker(marker, annotation); - } else { - // TiClusterRenderer is responsible for creating the Marker in this case. - // It is assigned to the TiMarker instance after it has been rendered in - // onClusterItemRendered callback. - tiMarker = new TiMarker(null, annotation); - if (mClusterManager != null) { - mClusterManager.addItem((TiMarker) tiMarker); - mClusterManager.cluster(); - } + annotation.processOptions(); + if (annotation.getProperty(MapModule.PROPERTY_CLUSTER_IDENTIFIER) == null) { + Marker marker = collection.addMarker(annotation.getMarkerOptions()); + tiMarker = new TiMarker(marker, annotation); + } else { + // TiClusterRenderer is responsible for creating the Marker in this case. + // It is assigned to the TiMarker instance after it has been rendered in + // onClusterItemRendered callback. + tiMarker = new TiMarker(null, annotation); + if (mClusterManager != null) { + mClusterManager.addItem(tiMarker); + mClusterManager.cluster(); } - annotation.setTiMarker(tiMarker); - timarkers.add(tiMarker); } + annotation.setTiMarker(tiMarker); + timarkers.add(tiMarker); } protected void addAnnotations(Object[] annotations) @@ -840,8 +866,10 @@ public void removePolygon(PolygonProxy p) public void removeAllPolygons() { for (PolygonProxy polygonProxy : currentPolygons) { - polygonProxy.getPolygon().remove(); - polygonProxy.setPolygon(null); + if (polygonProxy.getPolygon() != null) { + polygonProxy.getPolygon().remove(); + polygonProxy.setPolygon(null); + } } currentPolygons.clear(); } @@ -888,8 +916,10 @@ public void removePolyline(PolylineProxy p) public void removeAllPolylines() { for (PolylineProxy polylineProxy : currentPolylines) { - polylineProxy.getPolyline().remove(); - polylineProxy.setPolyline(null); + if (polylineProxy.getPolyline() != null) { + polylineProxy.getPolyline().remove(); + polylineProxy.setPolyline(null); + } } currentPolylines.clear(); } @@ -923,22 +953,29 @@ public void removeCircle(CircleProxy c) if (!currentCircles.contains(c)) { return; } - c.getCircle().remove(); - c.setCircle(null); + if (c.getCircle() != null) { + c.getCircle().remove(); + c.setCircle(null); + } currentCircles.remove(c); } public void removeAllCircles() { for (CircleProxy circleProxy : currentCircles) { - circleProxy.getCircle().remove(); - circleProxy.setCircle(null); + if (circleProxy.getCircle() != null) { + circleProxy.getCircle().remove(); + circleProxy.setCircle(null); + } } currentCircles.clear(); } public void addImageOverlay(ImageOverlayProxy proxy) { + if (map == null || currentImageOverlays.contains(proxy)) { + return; + } proxy.setGroundOverlay(map.addGroundOverlay(proxy.getGroundOverlayOptions())); currentImageOverlays.add(proxy); } @@ -946,8 +983,10 @@ public void addImageOverlay(ImageOverlayProxy proxy) public void removeImageOverlay(ImageOverlayProxy proxy) { if (currentImageOverlays.contains(proxy)) { - proxy.getGroundOverlay().remove(); - proxy.setGroundOverlay(null); + if (proxy.getGroundOverlay() != null) { + proxy.getGroundOverlay().remove(); + proxy.setGroundOverlay(null); + } currentImageOverlays.remove(proxy); } } @@ -955,8 +994,10 @@ public void removeImageOverlay(ImageOverlayProxy proxy) public void removeAllImageOverlays() { for (ImageOverlayProxy imageOverlayProxy : currentImageOverlays) { - imageOverlayProxy.getGroundOverlay().remove(); - imageOverlayProxy.setGroundOverlay(null); + if (imageOverlayProxy.getGroundOverlay() != null) { + imageOverlayProxy.getGroundOverlay().remove(); + imageOverlayProxy.setGroundOverlay(null); + } } currentImageOverlays.clear(); } @@ -1062,12 +1103,10 @@ public void firePinChangeDragStateEvent(Marker marker, AnnotationProxy annoProxy private String loadJSONFromAsset(String filename) { - String json = null; + String url = proxy.resolveUrl(null, filename); - try { - String url = proxy.resolveUrl(null, filename); - InputStream inputStream = TiFileFactory.createTitaniumFile(new String[] { url }, false).getInputStream(); - ByteArrayOutputStream result = new ByteArrayOutputStream(); + try (InputStream inputStream = TiFileFactory.createTitaniumFile(new String[] { url }, false).getInputStream(); + ByteArrayOutputStream result = new ByteArrayOutputStream()) { byte[] buffer = new byte[4096]; int length; @@ -1075,14 +1114,12 @@ private String loadJSONFromAsset(String filename) result.write(buffer, 0, length); } - json = result.toString("UTF-8"); - inputStream.close(); - result.close(); + return result.toString("UTF-8"); } catch (IOException ex) { Log.e(TAG, "Error opening file: " + ex.getMessage()); } - return json; + return null; } @Override @@ -1144,6 +1181,9 @@ public void onMapClick(LatLng point) for (CircleProxy circleProxy : clickableCircles) { Circle circle = circleProxy.getCircle(); + if (circle == null) { + continue; + } LatLng center = circle.getCenter(); double radius = circle.getRadius(); @@ -1304,13 +1344,18 @@ public void release() if (map != null) { map.clear(); } - currentCircles = null; - currentPolygons = null; - currentPolylines = null; - map = null; + // Clear rather than null out: these are read by removeXxx() paths which may still be + // reached from JS after the view was released. + currentCircles.clear(); + currentPolygons.clear(); + currentPolylines.clear(); + currentImageOverlays.clear(); timarkers.clear(); + map = null; + clusterRender = null; mClusterManager = null; mMarkerManager = null; + collection = null; super.release(); } @@ -1436,7 +1481,7 @@ public boolean onClusterClick(Cluster cluster) try { map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100)); } catch (Exception e) { - e.printStackTrace(); + Log.e(TAG, "Unable to zoom to cluster bounds", e); } } return true; diff --git a/android/src/ti/map/ViewProxy.java b/android/src/ti/map/ViewProxy.java index a09b9faf..38744476 100644 --- a/android/src/ti/map/ViewProxy.java +++ b/android/src/ti/map/ViewProxy.java @@ -10,9 +10,7 @@ import android.os.Message; import com.google.android.gms.maps.CameraUpdate; import com.google.android.gms.maps.GoogleMap; -import com.google.android.gms.maps.MapView; import com.google.android.gms.maps.model.LatLng; -import com.google.android.gms.maps.model.TileOverlay; import com.google.android.gms.maps.model.TileOverlayOptions; import com.google.maps.android.heatmaps.HeatmapTileProvider; import java.util.ArrayList; @@ -119,6 +117,7 @@ public void clearPreloadObjects() preloadPolygons.clear(); preloadPolylines.clear(); preloadCircles.clear(); + preloadOverlaysList.clear(); preloadTileOverlayOptionsList.clear(); } @@ -376,9 +375,12 @@ public void addAnnotation(AnnotationProxy annotation) private void handleAddAnnotation(AnnotationProxy annotation) { - TiUIMapView mapView = (TiUIMapView) peekView(); - if (mapView.getMap() != null) { - mapView.addAnnotation(annotation); + TiUIView view = peekView(); + if (view instanceof TiUIMapView) { + TiUIMapView mapView = (TiUIMapView) view; + if (mapView.getMap() != null) { + mapView.addAnnotation(annotation); + } } } @@ -450,7 +452,7 @@ public void showAnnotations(Object annotations) if (TiApplication.isUIThread()) { handleShowAnnotations(annotations); } else { - getMainHandler().obtainMessage(MSG_SHOW_ANNOTATIONS).sendToTarget(); + TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_SHOW_ANNOTATIONS), annotations); } } @@ -462,9 +464,12 @@ private void handleShowAnnotations(Object annotations) } Object[] annos = (Object[]) annotations; - TiUIMapView mapView = (TiUIMapView) peekView(); - if (mapView.getMap() != null) { - mapView.showAnnotations(annos); + TiUIView view = peekView(); + if (view instanceof TiUIMapView) { + TiUIMapView mapView = (TiUIMapView) view; + if (mapView.getMap() != null) { + mapView.showAnnotations(annos); + } } } @@ -602,9 +607,12 @@ public void handleRemoveAnnotations(Object[] annotations) public void handleRemoveAnnotation(Object annotation) { - TiUIMapView mapView = (TiUIMapView) peekView(); - if (mapView.getMap() != null) { - mapView.removeAnnotation(annotation); + TiUIView view = peekView(); + if (view instanceof TiUIMapView) { + TiUIMapView mapView = (TiUIMapView) view; + if (mapView.getMap() != null) { + mapView.removeAnnotation(annotation); + } } } @@ -972,7 +980,7 @@ public void addPolylines(Object polylinesObject) ArrayList polylinesList = new ArrayList(Arrays.asList((Object[]) polylines)); for (int i = 0; i < lines.length; i++) { Object polylineObject = lines[i]; - if (polylineObject instanceof AnnotationProxy) { + if (polylineObject instanceof PolylineProxy) { polylinesList.add(polylineObject); } } @@ -1216,10 +1224,12 @@ private float handleGetZoomLevel() { TiUIView view = peekView(); if (view instanceof TiUIMapView) { - return ((TiUIMapView) view).getMap().getCameraPosition().zoom; - } else { - return 0; + GoogleMap map = ((TiUIMapView) view).getMap(); + if (map != null) { + return map.getCameraPosition().zoom; + } } + return 0; } @Kroll.method @@ -1425,12 +1435,10 @@ public void removeImageOverlay(ImageOverlayProxy proxy) @Kroll.method public void removeAllImageOverlays() { - if (view instanceof TiUIMapView) { - if (TiApplication.isUIThread()) { - handleRemoveAllImageOverlays(); - } else { - TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_REMOVE_ALL_IMAGE_OVERLAYS)); - } + if (TiApplication.isUIThread()) { + handleRemoveAllImageOverlays(); + } else { + TiMessenger.sendBlockingMainMessage(getMainHandler().obtainMessage(MSG_REMOVE_ALL_IMAGE_OVERLAYS)); } }