From 6ec9b85c23272146f5a3ea986df6cac0fa0c5224 Mon Sep 17 00:00:00 2001 From: m0nac0 <58807793+m0nac0@users.noreply.github.com> Date: Fri, 20 May 2022 09:47:42 +0200 Subject: [PATCH 1/2] Cherry-pick upstream#815 (fixed android crash on style switch) https: //github.com/flutter-mapbox-gl/maps/pull/815 Co-Authored-By: Felix Horvat <24698503+felix-ht@users.noreply.github.com> --- .../mapbox/mapboxgl/MapboxMapController.java | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java index 68db8065a..79b03b96c 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java @@ -323,26 +323,30 @@ public void setStyleString(String styleString) { @Override public void onStyleLoaded(@NonNull Style style) { MapboxMapController.this.style = style; - final List orderReversed = new ArrayList(annotationOrder); - Collections.reverse(orderReversed); - String belowLayer = null; - - for(String annotationType : orderReversed) { - switch (annotationType) { - case "AnnotationType.fill": - belowLayer = enableFillManager(style, belowLayer); - break; - case "AnnotationType.line": - belowLayer = enableLineManager(style, belowLayer); - break; - case "AnnotationType.circle": - belowLayer = enableCircleManager(style, belowLayer); - break; - case "AnnotationType.symbol": - belowLayer = enableSymbolManager(style, belowLayer); - break; - default: - throw new IllegalArgumentException("Unknown annotation type: " + annotationType + ", must be either 'fill', 'line', 'circle' or 'symbol'"); + + // only add managers once to avoid issues with getLayerId after a style switch + if(symbolManager == null && circleManager == null && lineManager == null && fillManager == null) + { + final List orderReversed = new ArrayList(annotationOrder); + Collections.reverse(orderReversed); + String belowLayer = null; + for(String annotationType : orderReversed) { + switch (annotationType) { + case "AnnotationType.fill": + belowLayer = enableFillManager(style, belowLayer); + break; + case "AnnotationType.line": + belowLayer = enableLineManager(style, belowLayer); + break; + case "AnnotationType.circle": + belowLayer = enableCircleManager(style, belowLayer); + break; + case "AnnotationType.symbol": + belowLayer = enableSymbolManager(style, belowLayer); + break; + default: + throw new IllegalArgumentException("Unknown annotation type: " + annotationType + ", must be either 'fill', 'line', 'circle' or 'symbol'"); + } } } From 272eea2a06a91c979e24023304e270796576c391 Mon Sep 17 00:00:00 2001 From: m0nac0 <58807793+m0nac0@users.noreply.github.com> Date: Fri, 20 May 2022 09:53:31 +0200 Subject: [PATCH 2/2] Cherry-pick upstream#820 (HOTFIX Add option to not use annotations - android performance) https: //github.com/flutter-mapbox-gl/maps/pull/820 Co-Authored-By: Felix Horvat <24698503+felix-ht@users.noreply.github.com> --- .../com/mapbox/mapboxgl/MapboxMapBuilder.java | 1 - .../mapbox/mapboxgl/MapboxMapController.java | 103 ++++++++++++++++++ example/lib/layer.dart | 1 + example/lib/map_ui.dart | 88 ++++++++------- lib/src/mapbox_map.dart | 15 ++- 5 files changed, 161 insertions(+), 47 deletions(-) diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java index 1a0d963d3..e3f1f04d9 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java @@ -24,7 +24,6 @@ class MapboxMapBuilder implements MapboxMapOptionsSink { public final String TAG = getClass().getSimpleName(); private final MapboxMapOptions options = new MapboxMapOptions() - .textureMode(true) .attributionEnabled(true); private boolean trackCameraPosition = false; private boolean myLocationEnabled = false; diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java index 79b03b96c..171fe5c6d 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java @@ -157,6 +157,8 @@ final class MapboxMapController private List annotationConsumeTapEvents; private Set featureLayerIdentifiers; private LatLngBounds bounds = null; + private static final String annotationManagerNotCreatedErrorCode = "NO ANNOTATION MANAGER"; + private static final String annotationManagerNotCreatedErrorMessage = "To use %ss please add it to the annotation list"; MapboxMapController( int id, @@ -559,6 +561,7 @@ private Feature firstFeatureOnLayers(RectF in) { @Override public void onMethodCall(MethodCall call, MethodChannel.Result result) { + switch (call.method) { case "map#waitForMap": if (mapboxMap != null) { @@ -754,6 +757,10 @@ public void onError(@NonNull String message) { break; } case "symbols#addAll": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } List newSymbolIds = new ArrayList(); final List options = call.argument("options"); List symbolOptionsList = new ArrayList(); @@ -778,6 +785,10 @@ public void onError(@NonNull String message) { break; } case "symbols#removeAll": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final ArrayList symbolIds = call.argument("ids"); SymbolController symbolController; @@ -795,6 +806,10 @@ public void onError(@NonNull String message) { break; } case "symbol#update": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final String symbolId = call.argument("symbol"); final SymbolController symbol = symbol(symbolId); Convert.interpretSymbolOptions(call.argument("options"), symbol); @@ -803,6 +818,10 @@ public void onError(@NonNull String message) { break; } case "symbol#getGeometry": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final String symbolId = call.argument("symbol"); final SymbolController symbol = symbol(symbolId); final LatLng symbolLatLng = symbol.getGeometry(); @@ -812,30 +831,50 @@ public void onError(@NonNull String message) { result.success(hashMapLatLng); } case "symbolManager#iconAllowOverlap": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final Boolean value = call.argument("iconAllowOverlap"); symbolManager.setIconAllowOverlap(value); result.success(null); break; } case "symbolManager#iconIgnorePlacement": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final Boolean value = call.argument("iconIgnorePlacement"); symbolManager.setIconIgnorePlacement(value); result.success(null); break; } case "symbolManager#textAllowOverlap": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final Boolean value = call.argument("textAllowOverlap"); symbolManager.setTextAllowOverlap(value); result.success(null); break; } case "symbolManager#textIgnorePlacement": { + if(symbolManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "symbol"), null); + return; + } final Boolean iconAllowOverlap = call.argument("textIgnorePlacement"); symbolManager.setTextIgnorePlacement(iconAllowOverlap); result.success(null); break; } case "line#add": { + if(lineManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "line"), null); + return; + } final LineBuilder lineBuilder = newLineBuilder(); Convert.interpretLineOptions(call.argument("options"), lineBuilder); final Line line = lineBuilder.build(); @@ -845,12 +884,20 @@ public void onError(@NonNull String message) { break; } case "line#remove": { + if(lineManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "line"), null); + return; + } final String lineId = call.argument("line"); removeLine(lineId); result.success(null); break; } case "line#addAll": { + if(lineManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "line"), null); + return; + } List newIds = new ArrayList(); final List options = call.argument("options"); List optionList = new ArrayList(); @@ -875,6 +922,10 @@ public void onError(@NonNull String message) { break; } case "line#removeAll": { + if(lineManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "line"), null); + return; + } final ArrayList ids = call.argument("ids"); LineController lineController; @@ -892,6 +943,10 @@ public void onError(@NonNull String message) { break; } case "line#update": { + if(lineManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "line"), null); + return; + } final String lineId = call.argument("line"); final LineController line = line(lineId); Convert.interpretLineOptions(call.argument("options"), line); @@ -900,6 +955,10 @@ public void onError(@NonNull String message) { break; } case "line#getGeometry": { + if(lineManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "line"), null); + return; + } final String lineId = call.argument("line"); final LineController line = line(lineId); final List lineLatLngs = line.getGeometry(); @@ -914,6 +973,10 @@ public void onError(@NonNull String message) { break; } case "circle#add": { + if(circleManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "circle"), null); + return; + } final CircleBuilder circleBuilder = newCircleBuilder(); Convert.interpretCircleOptions(call.argument("options"), circleBuilder); final Circle circle = circleBuilder.build(); @@ -923,6 +986,10 @@ public void onError(@NonNull String message) { break; } case "circle#addAll": { + if(circleManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "circle"), null); + return; + } List newIds = new ArrayList(); final List options = call.argument("options"); List optionList = new ArrayList(); @@ -947,6 +1014,10 @@ public void onError(@NonNull String message) { break; } case "circle#removeAll": { + if(circleManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "circle"), null); + return; + } final ArrayList ids = call.argument("ids"); CircleController circleController; @@ -964,12 +1035,20 @@ public void onError(@NonNull String message) { break; } case "circle#remove": { + if(circleManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "circle"), null); + return; + } final String circleId = call.argument("circle"); removeCircle(circleId); result.success(null); break; } case "circle#update": { + if(circleManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "circle"), null); + return; + } Log.e(TAG, "update circle"); final String circleId = call.argument("circle"); final CircleController circle = circle(circleId); @@ -979,6 +1058,10 @@ public void onError(@NonNull String message) { break; } case "circle#getGeometry": { + if(circleManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "circle"), null); + return; + } final String circleId = call.argument("circle"); final CircleController circle = circle(circleId); final LatLng circleLatLng = circle.getGeometry(); @@ -989,6 +1072,10 @@ public void onError(@NonNull String message) { break; } case "fill#add": { + if(fillManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "fill"), null); + return; + } final FillBuilder fillBuilder = newFillBuilder(); Convert.interpretFillOptions(call.argument("options"), fillBuilder); final Fill fill = fillBuilder.build(); @@ -999,6 +1086,10 @@ public void onError(@NonNull String message) { } case "fill#addAll": { + if(fillManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "fill"), null); + return; + } List newIds = new ArrayList(); final List options = call.argument("options"); List optionList = new ArrayList(); @@ -1023,6 +1114,10 @@ public void onError(@NonNull String message) { break; } case "fill#removeAll": { + if(fillManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "fill"), null); + return; + } final ArrayList ids = call.argument("ids"); FillController fillController; @@ -1040,12 +1135,20 @@ public void onError(@NonNull String message) { break; } case "fill#remove": { + if(fillManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "fill"), null); + return; + } final String fillId = call.argument("fill"); removeFill(fillId); result.success(null); break; } case "fill#update": { + if(fillManager == null){ + result.error(annotationManagerNotCreatedErrorCode, String.format(annotationManagerNotCreatedErrorCode, "fill"), null); + return; + } final String fillId = call.argument("fill"); final FillController fill = fill(fillId); Convert.interpretFillOptions(call.argument("options"), fill); diff --git a/example/lib/layer.dart b/example/lib/layer.dart index 0aa8ac309..757533194 100644 --- a/example/lib/layer.dart +++ b/example/lib/layer.dart @@ -34,6 +34,7 @@ class LayerState extends State { target: center, zoom: 11.0, ), + annotationOrder: const [], ); } diff --git a/example/lib/map_ui.dart b/example/lib/map_ui.dart index 4c9732737..591b4bd87 100644 --- a/example/lib/map_ui.dart +++ b/example/lib/map_ui.dart @@ -39,9 +39,10 @@ class MapUiBodyState extends State { ); MaplibreMapController? mapController; - CameraPosition? _position = _kInitialPosition; + CameraPosition _position = _kInitialPosition; bool _isMoving = false; bool _compassEnabled = true; + bool _mapExpanded = true; CameraTargetBounds _cameraTargetBounds = CameraTargetBounds.unbounded; MinMaxZoomPreference _minMaxZoomPreference = MinMaxZoomPreference.unbounded; int _styleStringIndex = 0; @@ -74,7 +75,8 @@ class MapUiBodyState extends State { } void _extractMapInfo() { - _position = mapController!.cameraPosition; + final position = mapController!.cameraPosition; + if (position != null) _position = position; _isMoving = mapController!.isCameraMoving; } @@ -118,6 +120,17 @@ class MapUiBodyState extends State { ); } + Widget _mapSizeToggler() { + return TextButton( + child: Text('${_mapExpanded ? 'shrink' : 'expand'} map'), + onPressed: () { + setState(() { + _mapExpanded = !_mapExpanded; + }); + }, + ); + } + Widget _compassToggler() { return TextButton( child: Text('${_compassEnabled ? 'disable' : 'enable'} compasss'), @@ -345,53 +358,44 @@ class MapUiBodyState extends State { }, ); - final List columnChildren = [ - Padding( - padding: const EdgeInsets.all(10.0), - child: Center( - child: SizedBox( - width: 300.0, - height: 200.0, - child: mapboxMap, - ), + final List listViewChildren = [ + Center( + child: SizedBox( + width: _mapExpanded ? null : 300.0, + height: 200.0, + child: mapboxMap, ), ), ]; if (mapController != null) { - columnChildren.add( - Expanded( - child: ListView( - children: [ - Text('camera bearing: ${_position!.bearing}'), - Text( - 'camera target: ${_position!.target.latitude.toStringAsFixed(4)},' - '${_position!.target.longitude.toStringAsFixed(4)}'), - Text('camera zoom: ${_position!.zoom}'), - Text('camera tilt: ${_position!.tilt}'), - Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), - _queryFilterToggler(), - _compassToggler(), - _myLocationTrackingModeCycler(), - _latLngBoundsToggler(), - _setStyleToSatellite(), - _zoomBoundsToggler(), - _rotateToggler(), - _scrollToggler(), - _tiltToggler(), - _zoomToggler(), - _myLocationToggler(), - _telemetryToggler(), - _visibleRegionGetter(), - ], - ), - ), + listViewChildren.addAll( + [ + Text('camera bearing: ${_position.bearing}'), + Text('camera target: ${_position.target.latitude.toStringAsFixed(4)},' + '${_position.target.longitude.toStringAsFixed(4)}'), + Text('camera zoom: ${_position.zoom}'), + Text('camera tilt: ${_position.tilt}'), + Text(_isMoving ? '(Camera moving)' : '(Camera idle)'), + _mapSizeToggler(), + _queryFilterToggler(), + _compassToggler(), + _myLocationTrackingModeCycler(), + _latLngBoundsToggler(), + _setStyleToSatellite(), + _zoomBoundsToggler(), + _rotateToggler(), + _scrollToggler(), + _tiltToggler(), + _zoomToggler(), + _myLocationToggler(), + _telemetryToggler(), + _visibleRegionGetter(), + ], ); } - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: columnChildren, + return ListView( + children: listViewChildren, ); } diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index 0a70726b0..23448f8b1 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -51,15 +51,20 @@ class MaplibreMap extends StatefulWidget { AnnotationType.line, AnnotationType.circle, ], - }) : assert(annotationOrder.length == 4), + }) : assert(annotationOrder.length <= 4), assert(annotationConsumeTapEvents.length > 0), super(key: key); - /// Defined the layer order of annotations displayed on map - /// (must contain all annotation types, 4 items) + /// Defines the layer order of annotations displayed on map + /// + /// Any annotation type can only be contained once, so 0 to 4 types + /// + /// Note that setting this to be empty gives a big perfomance boost for + /// android. However if you do so annotations will not work. final List annotationOrder; - /// Defined the layer order of click annotations + /// Defines the layer order of click annotations + /// /// (must contain at least 1 annotation type, 4 items max) final List annotationConsumeTapEvents; @@ -208,6 +213,8 @@ class _MaplibreMapState extends State { Widget build(BuildContext context) { final List annotationOrder = widget.annotationOrder.map((e) => e.toString()).toList(); + assert(annotationOrder.toSet().length == annotationOrder.length, + "annotationOrder must not have duplicate types"); final List annotationConsumeTapEvents = widget.annotationConsumeTapEvents.map((e) => e.toString()).toList();