From 29bbfcccdecde7d3570dff418d10e460043ed409 Mon Sep 17 00:00:00 2001 From: tobrun Date: Mon, 25 Nov 2019 18:48:05 +0100 Subject: [PATCH] [android] remove platform specific Android code, migrate project to mapbox-gl-native-android --- Makefile | 303 - circle.yml | 253 - platform/android/.editorconfig | 4 - platform/android/.project | 17 - .../org.eclipse.buildship.core.prefs | 2 - platform/android/CHANGELOG.md | 5 +- platform/android/DISTRIBUTE.md | 57 - platform/android/LICENSE.md | 227 - .../android/MapboxGLAndroidSDK/.gitignore | 2 - .../android/MapboxGLAndroidSDK/build.gradle | 105 - .../MapboxGLAndroidSDK/gradle.properties | 9 - platform/android/MapboxGLAndroidSDK/lint.xml | 7 - .../MapboxGLAndroidSDK/proguard-rules.pro | 41 - .../src/main/AndroidManifest.xml | 15 - .../com/mapbox/mapboxsdk/AccountsManager.java | 170 - .../com/mapbox/mapboxsdk/LibraryLoader.java | 55 - .../mapboxsdk/LibraryLoaderProvider.java | 15 - .../com/mapbox/mapboxsdk/MapStrictMode.java | 46 - .../mapboxsdk/MapStrictModeException.java | 7 - .../java/com/mapbox/mapboxsdk/Mapbox.java | 237 - .../com/mapbox/mapboxsdk/ModuleProvider.java | 38 - .../mapbox/mapboxsdk/ModuleProviderImpl.java | 30 - .../mapboxsdk/annotations/Annotation.java | 153 - .../mapboxsdk/annotations/ArrowDirection.java | 31 - .../annotations/BaseMarkerOptions.java | 124 - .../annotations/BasePointCollection.java | 82 - .../mapbox/mapboxsdk/annotations/Bubble.java | 315 - .../mapboxsdk/annotations/BubbleLayout.java | 350 - .../annotations/BubblePopupHelper.java | 35 - .../mapbox/mapboxsdk/annotations/Icon.java | 124 - .../mapboxsdk/annotations/IconFactory.java | 188 - .../mapboxsdk/annotations/InfoWindow.java | 351 - .../mapbox/mapboxsdk/annotations/Marker.java | 278 - .../mapboxsdk/annotations/MarkerOptions.java | 195 - .../mapbox/mapboxsdk/annotations/Polygon.java | 109 - .../mapboxsdk/annotations/PolygonOptions.java | 308 - .../mapboxsdk/annotations/Polyline.java | 71 - .../annotations/PolylineOptions.java | 250 - .../mapboxsdk/annotations/package-info.java | 4 - .../mapboxsdk/attribution/Attribution.java | 70 - .../attribution/AttributionLayout.java | 68 - .../attribution/AttributionMeasure.java | 249 - .../attribution/AttributionParser.java | 314 - .../mapboxsdk/attribution/package-info.java | 4 - .../mapboxsdk/camera/CameraPosition.java | 414 - .../mapbox/mapboxsdk/camera/CameraUpdate.java | 23 - .../mapboxsdk/camera/CameraUpdateFactory.java | 637 - .../mapbox/mapboxsdk/camera/package-info.java | 4 - .../constants/GeometryConstants.java | 67 - .../mapboxsdk/constants/MapboxConstants.java | 232 - .../constants/TelemetryConstants.java | 14 - .../mapboxsdk/constants/package-info.java | 4 - .../CalledFromWorkerThreadException.java | 8 - .../exceptions/ConversionException.java | 11 - .../IconBitmapChangedException.java | 25 - .../InvalidLatLngBoundsException.java | 12 - .../InvalidMarkerPositionException.java | 16 - .../MapboxConfigurationException.java | 33 - .../exceptions/TooManyIconsException.java | 20 - .../mapboxsdk/exceptions/package-info.java | 4 - .../com/mapbox/mapboxsdk/geometry/LatLng.java | 334 - .../mapboxsdk/geometry/LatLngBounds.java | 608 - .../mapbox/mapboxsdk/geometry/LatLngQuad.java | 94 - .../mapbox/mapboxsdk/geometry/LatLngSpan.java | 139 - .../mapboxsdk/geometry/ProjectedMeters.java | 155 - .../mapboxsdk/geometry/VisibleRegion.java | 161 - .../mapboxsdk/geometry/package-info.java | 4 - .../mapbox/mapboxsdk/http/HttpIdentifier.java | 39 - .../com/mapbox/mapboxsdk/http/HttpLogger.java | 35 - .../mapbox/mapboxsdk/http/HttpRequest.java | 32 - .../mapbox/mapboxsdk/http/HttpRequestUrl.java | 53 - .../mapbox/mapboxsdk/http/HttpResponder.java | 32 - .../mapboxsdk/http/LocalRequestTask.java | 75 - .../mapboxsdk/http/NativeHttpRequest.java | 90 - .../mapbox/mapboxsdk/http/package-info.java | 4 - .../location/AnimatorListenerHolder.java | 45 - .../mapboxsdk/location/CompassEngine.java | 55 - .../mapboxsdk/location/CompassListener.java | 33 - .../mapboxsdk/location/LatLngEvaluator.java | 21 - .../location/LayerBitmapProvider.java | 32 - .../location/LayerFeatureProvider.java | 26 - .../location/LayerSourceProvider.java | 111 - .../location/LocationAnimatorCoordinator.java | 422 - .../location/LocationCameraController.java | 437 - .../mapboxsdk/location/LocationComponent.java | 1618 - .../LocationComponentActivationOptions.java | 244 - .../LocationComponentCompassEngine.java | 268 - .../location/LocationComponentConstants.java | 94 - ...ationComponentNotInitializedException.java | 8 - .../location/LocationComponentOptions.java | 1778 - .../LocationComponentPositionManager.java | 48 - .../location/LocationLayerController.java | 464 - .../mapboxsdk/location/MapboxAnimator.java | 95 - .../location/MapboxAnimatorProvider.java | 40 - .../location/MapboxAnimatorSetProvider.java | 32 - .../location/MapboxCameraAnimatorAdapter.java | 36 - .../location/MapboxFloatAnimator.java | 17 - .../location/MapboxLatLngAnimator.java | 20 - .../OnCameraMoveInvalidateListener.java | 7 - .../OnCameraTrackingChangedListener.java | 21 - .../OnLocationCameraTransitionListener.java | 24 - .../location/OnLocationClickListener.java | 14 - .../location/OnLocationLongClickListener.java | 14 - .../location/OnLocationStaleListener.java | 17 - .../location/OnRenderModeChangedListener.java | 16 - .../mapboxsdk/location/StaleStateManager.java | 99 - .../com/mapbox/mapboxsdk/location/Utils.java | 121 - .../mapboxsdk/location/modes/CameraMode.java | 66 - .../mapboxsdk/location/modes/RenderMode.java | 45 - .../location/modes/package-info.java | 4 - .../mapboxsdk/location/package-info.java | 4 - .../java/com/mapbox/mapboxsdk/log/Logger.java | 321 - .../mapboxsdk/log/LoggerDefinition.java | 102 - .../mapbox/mapboxsdk/log/package-info.java | 4 - .../mapboxsdk/maps/AnnotationContainer.java | 87 - .../mapboxsdk/maps/AnnotationManager.java | 519 - .../mapbox/mapboxsdk/maps/Annotations.java | 25 - .../maps/AttributionDialogManager.java | 244 - .../maps/CameraChangeDispatcher.java | 204 - .../maps/FocalPointChangeListener.java | 11 - .../mapbox/mapboxsdk/maps/IconManager.java | 155 - .../java/com/mapbox/mapboxsdk/maps/Image.java | 22 - .../mapboxsdk/maps/InfoWindowManager.java | 96 - .../mapboxsdk/maps/MapChangeReceiver.java | 395 - .../mapbox/mapboxsdk/maps/MapFragment.java | 241 - .../mapboxsdk/maps/MapGestureDetector.java | 1140 - .../mapbox/mapboxsdk/maps/MapKeyListener.java | 271 - .../com/mapbox/mapboxsdk/maps/MapView.java | 1350 - .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 2425 - .../mapboxsdk/maps/MapboxMapOptions.java | 1283 - .../mapboxsdk/maps/MarkerContainer.java | 146 - .../com/mapbox/mapboxsdk/maps/Markers.java | 27 - .../com/mapbox/mapboxsdk/maps/NativeMap.java | 274 - .../mapbox/mapboxsdk/maps/NativeMapView.java | 1512 - .../mapboxsdk/maps/OnMapReadyCallback.java | 22 - .../mapboxsdk/maps/PolygonContainer.java | 84 - .../com/mapbox/mapboxsdk/maps/Polygons.java | 22 - .../mapboxsdk/maps/PolylineContainer.java | 83 - .../com/mapbox/mapboxsdk/maps/Polylines.java | 22 - .../com/mapbox/mapboxsdk/maps/Projection.java | 283 - .../maps/ShapeAnnotationContainer.java | 41 - .../mapboxsdk/maps/ShapeAnnotations.java | 13 - .../java/com/mapbox/mapboxsdk/maps/Style.java | 1150 - .../mapboxsdk/maps/SupportMapFragment.java | 219 - .../mapboxsdk/maps/TelemetryDefinition.java | 66 - .../com/mapbox/mapboxsdk/maps/Transform.java | 341 - .../com/mapbox/mapboxsdk/maps/UiSettings.java | 1103 - .../mapbox/mapboxsdk/maps/package-info.java | 4 - .../mapboxsdk/maps/renderer/MapRenderer.java | 158 - .../maps/renderer/MapRendererRunnable.java | 32 - .../maps/renderer/MapRendererScheduler.java | 17 - .../maps/renderer/egl/EGLConfigChooser.java | 328 - .../maps/renderer/egl/EGLConfigException.java | 21 - .../maps/renderer/egl/package-info.java | 4 - .../GLSurfaceViewMapRenderer.java | 115 - .../glsurfaceview/MapboxGLSurfaceView.java | 54 - .../renderer/glsurfaceview/package-info.java | 4 - .../mapboxsdk/maps/renderer/package-info.java | 4 - .../textureview/TextureViewMapRenderer.java | 115 - .../textureview/TextureViewRenderThread.java | 470 - .../renderer/textureview/package-info.java | 4 - .../mapboxsdk/maps/widgets/CompassView.java | 179 - .../mapboxsdk/maps/widgets/package-info.java | 4 - .../module/http/HttpRequestImpl.java | 196 - .../module/http/HttpRequestUtil.java | 71 - .../mapboxsdk/module/http/package-info.java | 4 - .../loader/LibraryLoaderProviderImpl.java | 49 - .../mapboxsdk/module/loader/package-info.java | 4 - .../module/telemetry/MapBaseEvent.java | 39 - .../module/telemetry/MapEventFactory.java | 57 - .../module/telemetry/MapLoadEvent.java | 179 - .../telemetry/OfflineDownloadEndEvent.java | 140 - .../telemetry/OfflineDownloadStartEvent.java | 98 - .../module/telemetry/PerformanceEvent.java | 179 - .../module/telemetry/PhoneState.java | 171 - .../module/telemetry/TelemetryImpl.java | 136 - .../module/telemetry/package-info.java | 4 - .../mapboxsdk/net/ConnectivityListener.java | 10 - .../mapboxsdk/net/ConnectivityReceiver.java | 160 - .../net/NativeConnectivityListener.java | 44 - .../mapbox/mapboxsdk/net/package-info.java | 4 - .../OfflineGeometryRegionDefinition.java | 175 - .../mapboxsdk/offline/OfflineManager.java | 748 - .../offline/OfflineRegionDefinition.java | 72 - .../mapboxsdk/offline/OfflineRegionError.java | 92 - .../offline/OfflineRegionStatus.java | 172 - .../OfflineTilePyramidRegionDefinition.java | 162 - .../mapboxsdk/offline/package-info.java | 4 - .../com/mapbox/mapboxsdk/package-info.java | 4 - .../mapboxsdk/snapshotter/MapSnaphotUtil.java | 29 - .../mapboxsdk/snapshotter/MapSnapshot.java | 81 - .../mapboxsdk/snapshotter/MapSnapshotter.java | 661 - .../mapboxsdk/snapshotter/package-info.java | 4 - .../mapbox/mapboxsdk/storage/FileSource.java | 412 - .../mapbox/mapboxsdk/storage/Resource.java | 60 - .../mapboxsdk/storage/package-info.java | 4 - .../style/expressions/Expression.java | 4934 - .../style/expressions/package-info.java | 4 - .../style/layers/BackgroundLayer.java | 216 - .../style/layers/CannotAddLayerException.java | 15 - .../mapboxsdk/style/layers/CircleLayer.java | 584 - .../mapboxsdk/style/layers/CustomLayer.java | 37 - .../style/layers/FillExtrusionLayer.java | 463 - .../mapboxsdk/style/layers/FillLayer.java | 436 - .../mapboxsdk/style/layers/HeatmapLayer.java | 331 - .../style/layers/HillshadeLayer.java | 360 - .../mapbox/mapboxsdk/style/layers/Layer.java | 168 - .../style/layers/LayoutPropertyValue.java | 11 - .../mapboxsdk/style/layers/LineLayer.java | 676 - .../style/layers/PaintPropertyValue.java | 11 - .../mapboxsdk/style/layers/Property.java | 696 - .../style/layers/PropertyFactory.java | 2558 - .../mapboxsdk/style/layers/PropertyValue.java | 127 - .../mapboxsdk/style/layers/RasterLayer.java | 397 - .../mapboxsdk/style/layers/SymbolLayer.java | 1436 - .../style/layers/TransitionOptions.java | 136 - .../mapboxsdk/style/layers/layer.java.ejs | 250 - .../mapboxsdk/style/layers/package-info.java | 4 - .../mapboxsdk/style/layers/property.java.ejs | 61 - .../style/layers/property_factory.java.ejs | 100 - .../mapbox/mapboxsdk/style/light/Light.java | 242 - .../mapboxsdk/style/light/Position.java | 90 - .../mapboxsdk/style/light/light.java.ejs | 154 - .../mapboxsdk/style/light/package-info.java | 4 - .../sources/CannotAddSourceException.java | 15 - .../style/sources/CustomGeometrySource.java | 379 - .../sources/CustomGeometrySourceOptions.java | 85 - .../style/sources/GeoJsonOptions.java | 139 - .../style/sources/GeoJsonSource.java | 518 - .../style/sources/GeometryTileProvider.java | 22 - .../mapboxsdk/style/sources/ImageSource.java | 271 - .../style/sources/RasterDemSource.java | 185 - .../mapboxsdk/style/sources/RasterSource.java | 182 - .../mapboxsdk/style/sources/Source.java | 91 - .../mapboxsdk/style/sources/TileSet.java | 341 - .../style/sources/UnknownSource.java | 27 - .../mapboxsdk/style/sources/VectorSource.java | 171 - .../mapboxsdk/style/sources/package-info.java | 4 - .../mapboxsdk/style/types/Formatted.java | 69 - .../style/types/FormattedSection.java | 240 - .../mapboxsdk/style/types/package-info.java | 4 - .../mapboxsdk/text/LocalGlyphRasterizer.java | 59 - .../mapbox/mapboxsdk/text/package-info.java | 4 - .../mapbox/mapboxsdk/utils/AnimatorUtils.java | 168 - .../mapbox/mapboxsdk/utils/BitmapUtils.java | 189 - .../mapbox/mapboxsdk/utils/ColorUtils.java | 204 - .../com/mapbox/mapboxsdk/utils/Compare.java | 27 - .../com/mapbox/mapboxsdk/utils/FileUtils.java | 147 - .../com/mapbox/mapboxsdk/utils/FontUtils.java | 81 - .../mapboxsdk/utils/MapFragmentUtils.java | 51 - .../com/mapbox/mapboxsdk/utils/MathUtils.java | 64 - .../mapbox/mapboxsdk/utils/StringUtils.java | 27 - .../mapbox/mapboxsdk/utils/ThreadUtils.java | 48 - .../mapbox/mapboxsdk/utils/package-info.java | 4 - .../src/main/java/overview.html | 19 - .../src/main/res-public/values/public.xml | 163 - .../res/drawable-hdpi/mapbox_compass_icon.png | Bin 3787 -> 0 bytes .../res/drawable-hdpi/mapbox_logo_helmet.png | Bin 1650 -> 0 bytes .../res/drawable-hdpi/mapbox_logo_icon.png | Bin 4778 -> 0 bytes .../mapbox_marker_icon_default.png | Bin 1520 -> 0 bytes .../mapbox_markerview_icon_default.png | Bin 1669 -> 0 bytes .../mapbox_mylocation_icon_bearing.png | Bin 1046 -> 0 bytes .../mapbox_mylocation_icon_default.png | Bin 885 -> 0 bytes .../res/drawable-mdpi/mapbox_compass_icon.png | Bin 2488 -> 0 bytes .../res/drawable-mdpi/mapbox_logo_helmet.png | Bin 950 -> 0 bytes .../res/drawable-mdpi/mapbox_logo_icon.png | Bin 2622 -> 0 bytes .../mapbox_marker_icon_default.png | Bin 1010 -> 0 bytes .../mapbox_markerview_icon_default.png | Bin 1115 -> 0 bytes .../mapbox_mylocation_icon_bearing.png | Bin 649 -> 0 bytes .../mapbox_mylocation_icon_default.png | Bin 555 -> 0 bytes .../drawable-xhdpi/mapbox_compass_icon.png | Bin 4775 -> 0 bytes .../res/drawable-xhdpi/mapbox_logo_helmet.png | Bin 2184 -> 0 bytes .../res/drawable-xhdpi/mapbox_logo_icon.png | Bin 6579 -> 0 bytes .../mapbox_marker_icon_default.png | Bin 1995 -> 0 bytes .../mapbox_markerview_icon_default.png | Bin 2163 -> 0 bytes .../mapbox_mylocation_icon_bearing.png | Bin 1345 -> 0 bytes .../mapbox_mylocation_icon_default.png | Bin 1096 -> 0 bytes .../drawable-xxhdpi/mapbox_compass_icon.png | Bin 7527 -> 0 bytes .../drawable-xxhdpi/mapbox_logo_helmet.png | Bin 3389 -> 0 bytes .../res/drawable-xxhdpi/mapbox_logo_icon.png | Bin 10802 -> 0 bytes .../mapbox_marker_icon_default.png | Bin 2998 -> 0 bytes .../mapbox_markerview_icon_default.png | Bin 3163 -> 0 bytes .../mapbox_mylocation_icon_bearing.png | Bin 1902 -> 0 bytes .../mapbox_mylocation_icon_default.png | Bin 1586 -> 0 bytes .../drawable-xxxhdpi/mapbox_compass_icon.png | Bin 18537 -> 0 bytes .../drawable-xxxhdpi/mapbox_logo_helmet.png | Bin 4468 -> 0 bytes .../res/drawable-xxxhdpi/mapbox_logo_icon.png | Bin 14441 -> 0 bytes .../mapbox_marker_icon_default.png | Bin 4006 -> 0 bytes .../mapbox_markerview_icon_default.png | Bin 4071 -> 0 bytes .../mapbox_mylocation_icon_bearing.png | Bin 3022 -> 0 bytes .../mapbox_mylocation_icon_default.png | Bin 2456 -> 0 bytes .../res/drawable/mapbox_info_bg_selector.xml | 5 - .../res/drawable/mapbox_info_icon_default.xml | 9 - .../drawable/mapbox_info_icon_selected.xml | 9 - .../drawable/mapbox_mylocation_bg_shape.xml | 10 - .../mapbox_popup_window_transparent.xml | 12 - .../res/drawable/mapbox_rounded_corner.xml | 10 - .../res/drawable/mapbox_user_bearing_icon.xml | 10 - .../main/res/drawable/mapbox_user_icon.xml | 10 - .../res/drawable/mapbox_user_icon_shadow.xml | 19 - .../res/drawable/mapbox_user_icon_stale.xml | 10 - .../res/drawable/mapbox_user_puck_icon.xml | 17 - .../res/drawable/mapbox_user_stroke_icon.xml | 10 - .../layout/mapbox_attribution_list_item.xml | 14 - .../res/layout/mapbox_infowindow_content.xml | 53 - .../res/layout/mapbox_mapview_internal.xml | 27 - .../res/layout/mapbox_view_image_marker.xml | 6 - .../src/main/res/values-bg/strings.xml | 15 - .../src/main/res/values-ca/strings.xml | 15 - .../src/main/res/values-cs/strings.xml | 17 - .../src/main/res/values-es/strings.xml | 17 - .../src/main/res/values-fr/strings.xml | 17 - .../src/main/res/values-gl/strings.xml | 17 - .../src/main/res/values-he/strings.xml | 16 - .../src/main/res/values-hu/strings.xml | 15 - .../src/main/res/values-iw/strings.xml | 16 - .../src/main/res/values-ko/strings.xml | 16 - .../src/main/res/values-lt/strings.xml | 15 - .../src/main/res/values-nl/strings.xml | 15 - .../src/main/res/values-pl/strings.xml | 17 - .../src/main/res/values-pt-rPT/strings.xml | 16 - .../src/main/res/values-ru/strings.xml | 16 - .../src/main/res/values-sv/strings.xml | 16 - .../src/main/res/values-uk/strings.xml | 16 - .../src/main/res/values-vi/strings.xml | 17 - .../src/main/res/values-zh-rCN/strings.xml | 17 - .../src/main/res/values-zh-rHK/strings.xml | 17 - .../src/main/res/values-zh-rTW/strings.xml | 17 - .../src/main/res/values/attrs.xml | 187 - .../src/main/res/values/colors.xml | 9 - .../src/main/res/values/dimens.xml | 29 - .../src/main/res/values/strings.xml | 30 - .../src/main/res/values/styles.xml | 37 - .../mapbox/mapboxsdk/AccountsManagerTest.java | 59 - .../com/mapbox/mapboxsdk/MapboxInjector.java | 48 - .../java/com/mapbox/mapboxsdk/MapboxTest.java | 109 - .../mapboxsdk/annotations/AnnotationTest.java | 93 - .../mapboxsdk/annotations/IconTest.java | 56 - .../mapboxsdk/annotations/InfoWindowTest.java | 86 - .../mapboxsdk/annotations/MarkerTest.java | 160 - .../mapboxsdk/annotations/PolygonTest.java | 75 - .../mapboxsdk/annotations/PolylineTest.java | 75 - .../attribution/AttributionParseTest.java | 322 - .../mapboxsdk/camera/CameraPositionTest.java | 131 - .../mapboxsdk/constants/AppConstant.java | 6 - .../mapboxsdk/geometry/LatLngBoundsTest.java | 813 - .../mapboxsdk/geometry/LatLngSpanTest.java | 65 - .../mapbox/mapboxsdk/geometry/LatLngTest.java | 286 - .../geometry/ProjectedMetersTest.java | 66 - .../mapboxsdk/geometry/VisibleRegionTest.java | 95 - .../mapboxsdk/location/CompassEngineTest.java | 89 - .../LocationAnimatorCoordinatorTest.kt | 532 - .../LocationCameraControllerTest.java | 1001 - ...ocationComponentActivationOptionsTest.java | 119 - .../LocationComponentOptionsTest.java | 81 - .../LocationComponentPositionManagerTest.kt | 130 - .../location/LocationComponentTest.kt | 453 - .../location/LocationLayerControllerTest.java | 718 - .../mapboxsdk/location/MapboxAnimatorTest.kt | 43 - .../mapbox/mapboxsdk/location/UtilsTest.java | 28 - .../com/mapbox/mapboxsdk/log/LoggerTest.kt | 93 - .../mapboxsdk/maps/AnnotationManagerTest.java | 86 - .../maps/AttributionDialogManagerTest.java | 99 - .../mapboxsdk/maps/MapChangeReceiverTest.java | 586 - .../mapboxsdk/maps/MapTouchListenersTest.java | 157 - .../maps/MapboxMapOptionsAttrsTest.kt | 101 - .../mapboxsdk/maps/MapboxMapOptionsTest.java | 196 - .../mapbox/mapboxsdk/maps/MapboxMapTest.kt | 214 - .../mapbox/mapboxsdk/maps/StyleBuilderTest.kt | 237 - .../com/mapbox/mapboxsdk/maps/StyleTest.kt | 408 - .../mapbox/mapboxsdk/maps/TransformTest.kt | 183 - .../mapbox/mapboxsdk/maps/UiSettingsTest.java | 433 - .../module/http/HttpRequestUrlTest.kt | 67 - .../module/http/HttpRequestUtilTest.kt | 35 - .../module/telemetry/MapEventFactoryTest.java | 149 - .../module/telemetry/SchemaTest.java | 270 - .../style/expressions/ExpressionTest.java | 1537 - .../telemetry/HttpTransportTest.java | 20 - .../mapbox/mapboxsdk/utils/ColorUtilsTest.kt | 22 - .../mapbox/mapboxsdk/utils/MockParcel.java | 267 - .../org.mockito.plugins.MockMaker | 1 - .../android/MapboxGLAndroidSDKLint/.gitignore | 1 - .../MapboxGLAndroidSDKLint/build.gradle | 37 - .../com/mapbox/mapboxsdk/lint/KeepDetector.kt | 75 - .../mapboxsdk/lint/MapboxIssueRegistry.kt | 13 - .../mapbox/mapboxsdk/lint/KeepDetectorTest.kt | 28 - .../MapboxGLAndroidSDKTestApp/.gitignore | 2 - .../MapboxGLAndroidSDKTestApp/README.md | 11 - .../MapboxGLAndroidSDKTestApp/build.gradle | 93 - .../lint-baseline-local.xml | 166 - .../lint/lint-baseline-ci.xml | 177 - .../proguard-rules.pro | 14 - .../src/androidTest/AndroidManifest.xml | 4 - .../src/androidTest/assets/streets.json | 5331 - .../java/com/mapbox/mapboxsdk/AppCenter.kt | 15 - .../mapboxsdk/InstrumentationApplication.kt | 10 - .../mapbox/mapboxsdk/InstrumentationRunner.kt | 11 - .../camera/CameraUpdateFactoryTest.kt | 186 - .../integration/BaseIntegrationTest.kt | 40 - .../integration/FragmentBackStackTest.kt | 45 - .../integration/GLSurfaceViewReopenTest.kt | 29 - .../integration/GLSurfaceViewReuseTest.kt | 29 - .../integration/OrientationChangeTest.kt | 32 - .../QueryRenderedFeaturesBoxCountTest.kt | 30 - .../integration/TextureViewReopenTest.kt | 29 - .../integration/TextureViewReuseTest.kt | 29 - .../integration/ViewPagerScrollTest.kt | 36 - .../location/LocationComponentTest.kt | 1663 - .../location/LocationLayerControllerTest.kt | 609 - .../location/utils/LocationComponentAction.kt | 38 - .../utils/MapboxLocationTestingUtils.kt | 79 - .../utils/StyleChangeIdlingResource.kt | 40 - .../mapbox/mapboxsdk/maps/BaseLayerTest.kt | 27 - .../mapboxsdk/maps/GesturesUiTestUtils.kt | 516 - .../mapboxsdk/maps/IconManagerResolver.java | 42 - .../mapboxsdk/maps/MapGestureDetectorTest.kt | 193 - .../mapbox/mapboxsdk/maps/MapboxMapTest.java | 477 - .../com/mapbox/mapboxsdk/maps/MapboxTest.java | 111 - .../mapboxsdk/maps/NativeMapViewTest.kt | 421 - .../mapbox/mapboxsdk/maps/TransformTest.kt | 33 - .../mapboxsdk/maps/VisibleRegionTest.kt | 400 - .../mapboxsdk/offline/OfflineDownloadTest.kt | 85 - .../snapshotter/MapSnapshotterTest.kt | 57 - .../testapp/action/MapboxMapAction.java | 50 - .../testapp/action/OrientationAction.java | 78 - .../mapboxsdk/testapp/action/WaitAction.java | 39 - .../mapboxsdk/testapp/activity/BaseTest.java | 113 - .../testapp/activity/EspressoTest.java | 28 - .../testapp/activity/activity.junit.ejs | 32 - .../testapp/annotations/IconTest.java | 138 - .../testapp/annotations/MarkerTest.java | 63 - .../testapp/annotations/PolygonTest.java | 46 - .../testapp/annotations/PolylineTest.java | 42 - .../testapp/camera/CameraAnimateTest.java | 11 - .../testapp/camera/CameraEaseTest.java | 12 - .../testapp/camera/CameraForTest.java | 260 - .../testapp/camera/CameraMoveTest.java | 12 - .../mapboxsdk/testapp/camera/CameraTest.java | 307 - .../testapp/fragment/MapDialogFragmentTest.kt | 35 - .../geometry/GeoJsonConversionTest.java | 148 - .../testapp/geometry/LatLngBoundsTest.java | 65 - .../testapp/maps/ImageMissingTest.kt | 155 - .../testapp/maps/RemoveUnusedImagesTest.kt | 163 - .../mapboxsdk/testapp/maps/StyleLoadTest.kt | 31 - .../testapp/maps/widgets/AttributionTest.java | 217 - .../testapp/maps/widgets/CompassViewTest.java | 67 - .../testapp/maps/widgets/LogoTest.java | 60 - .../mapboxsdk/testapp/offline/CacheTest.kt | 104 - .../testapp/offline/OfflineManagerTest.kt | 124 - .../testapp/offline/OfflineUtilsTest.java | 41 - .../mapboxsdk/testapp/render/RenderTest.java | 74 - .../testapp/storage/FileSourceMapTest.kt | 59 - .../storage/FileSourceStandaloneTest.kt | 116 - .../testapp/storage/FileSourceTestUtils.kt | 59 - .../testapp/string/UppperLowerCaseTest.java | 47 - .../testapp/style/BackgroundLayerTest.java | 141 - .../testapp/style/CircleLayerTest.java | 453 - .../testapp/style/CustomGeometrySourceTest.kt | 69 - .../testapp/style/ExpressionTest.java | 786 - .../testapp/style/FillExtrusionLayerTest.java | 340 - .../testapp/style/FillLayerTest.java | 326 - .../testapp/style/GeoJsonSourceTests.java | 189 - .../testapp/style/HeatmapLayerTest.java | 215 - .../testapp/style/HillshadeLayerTest.java | 223 - .../mapboxsdk/testapp/style/ImageTest.kt | 75 - .../mapboxsdk/testapp/style/LightTest.java | 172 - .../testapp/style/LineLayerTest.java | 493 - .../testapp/style/RasterLayerTest.java | 240 - .../testapp/style/RuntimeStyleTests.java | 370 - .../style/RuntimeStyleTimingTests.java | 27 - .../testapp/style/StyleLoaderTest.java | 59 - .../testapp/style/SymbolLayerTest.java | 1313 - .../mapboxsdk/testapp/style/layer.junit.ejs | 199 - .../mapboxsdk/testapp/style/light.junit.ejs | 132 - .../utils/SnapshotterIdlingResource.java | 38 - .../testapp/utils/TestConstants.java | 12 - .../testapp/utils/TestingAsyncUtils.kt | 18 - .../mapbox/mapboxsdk/utils/FontUtilsTest.java | 41 - .../src/main/AndroidManifest.xml | 1029 - .../src/main/assets/Roboto-Medium.ttf | Bin 127488 -> 0 bytes .../src/main/assets/Roboto-Regular.ttf | Bin 126072 -> 0 bytes .../src/main/assets/commercial-24.png | Bin 308 -> 0 bytes .../src/main/assets/dog-park-24.png | Bin 831 -> 0 bytes .../src/main/assets/earthquakes.geojson | 6113 - .../src/main/assets/fill_filter_style.json | 10976 -- .../0-255.pbf | Bin 75287 -> 0 bytes .../0-255.pbf | Bin 73518 -> 0 bytes .../0-255.pbf | Bin 72149 -> 0 bytes .../0-255.pbf | Bin 75290 -> 0 bytes .../8192-8447.pbf | Bin 30553 -> 0 bytes .../src/main/assets/heavy_style.json | 81857 --------- .../src/main/assets/line_filter_style.json | 8746 - .../src/main/assets/london-underground-24.png | Bin 729 -> 0 bytes .../src/main/assets/numeric_filter_style.json | 8734 - .../src/main/assets/offline_test.db | Bin 73728 -> 0 bytes .../src/main/assets/points-sf.geojson | 115 - .../src/main/assets/points.geojson | 136346 --------------- .../src/main/assets/small_line.geojson | 1 - .../src/main/assets/small_poly.geojson | 1 - .../src/main/assets/streets.json | 10398 -- .../src/main/assets/town-hall-24.png | Bin 552 -> 0 bytes .../mapboxsdk/testapp/MapboxApplication.java | 95 - .../activity/FeatureOverviewActivity.java | 168 - .../AnimatedSymbolLayerActivity.java | 443 - .../annotation/BulkMarkerActivity.java | 203 - .../DynamicMarkerChangeActivity.java | 120 - .../activity/annotation/PolygonActivity.java | 219 - .../activity/annotation/PolylineActivity.java | 224 - .../annotation/PressForMarkerActivity.java | 152 - .../camera/CameraAnimationTypeActivity.java | 173 - .../camera/CameraAnimatorActivity.java | 281 - .../camera/CameraPositionActivity.java | 279 - .../camera/GestureDetectorActivity.java | 456 - .../activity/camera/LatLngBoundsActivity.kt | 165 - .../activity/camera/ManualZoomActivity.java | 120 - .../activity/camera/MaxMinZoomActivity.java | 99 - .../activity/camera/ScrollByActivity.java | 158 - .../customlayer/CustomLayerActivity.java | 143 - .../DeviceIndependentTestActivity.java | 79 - .../espresso/EspressoTestActivity.java | 66 - .../activity/espresso/PixelTestActivity.kt | 66 - ...QueryRenderedFeaturesBoxCountActivity.java | 154 - ...yRenderedFeaturesBoxHighlightActivity.java | 130 - ...enderedFeaturesBoxSymbolCountActivity.java | 145 - ...eryRenderedFeaturesPropertiesActivity.java | 244 - .../feature/QuerySourceFeaturesActivity.java | 134 - .../fragment/FragmentBackStackActivity.kt | 67 - .../fragment/MapFragmentActivity.java | 97 - .../activity/fragment/MultiMapActivity.java | 31 - .../fragment/NestedViewPagerActivity.kt | 166 - .../fragment/SupportMapFragmentActivity.java | 96 - .../activity/fragment/ViewPagerActivity.kt | 102 - .../imagegenerator/PrintActivity.java | 93 - .../imagegenerator/SnapshotActivity.kt | 89 - .../DynamicInfoWindowAdapterActivity.java | 158 - .../infowindow/InfoWindowActivity.java | 199 - .../infowindow/InfoWindowAdapterActivity.java | 128 - .../LocationComponentActivationActivity.java | 140 - .../location/LocationFragmentActivity.kt | 176 - .../location/LocationMapChangeActivity.java | 142 - .../location/LocationModesActivity.java | 432 - .../ManualLocationUpdatesActivity.java | 170 - .../testapp/activity/location/Utils.java | 59 - .../maplayout/BottomSheetActivity.java | 282 - .../activity/maplayout/DebugModeActivity.java | 306 - .../activity/maplayout/DoubleMapActivity.java | 160 - .../GLSurfaceRecyclerViewActivity.kt | 154 - .../LatLngBoundsForCameraActivity.java | 154 - .../maplayout/LocalGlyphActivity.java | 80 - .../activity/maplayout/MapChangeActivity.java | 89 - .../maplayout/MapInDialogActivity.java | 110 - .../maplayout/MapPaddingActivity.java | 131 - .../activity/maplayout/OverlayMapActivity.kt | 86 - .../activity/maplayout/SimpleMapActivity.java | 89 - .../maplayout/TextureRecyclerViewActivity.kt | 15 - .../maplayout/VisibilityChangeActivity.java | 144 - .../ChangeResourcesCachePathActivity.kt | 180 - .../offline/DeleteRegionActivity.java | 175 - .../offline/DownloadRegionActivity.kt | 319 - .../offline/MergeOfflineRegionsActivity.kt | 147 - .../activity/offline/OfflineActivity.java | 320 - .../offline/UpdateMetadataActivity.java | 187 - .../activity/render/RenderTestActivity.java | 366 - .../activity/render/RenderTestDefinition.java | 91 - .../render/RenderTestSnapshotter.java | 18 - .../render/RenderTestStyleDefinition.java | 131 - .../snapshot/MapSnapshotterActivity.java | 128 - .../MapSnapshotterLocalStyleActivity.java | 71 - .../MapSnapshotterMarkerActivity.java | 105 - .../snapshot/MapSnapshotterReuseActivity.java | 103 - .../storage/CacheManagementActivity.kt | 77 - .../storage/UrlTransformActivity.java | 99 - .../style/AnimatedImageSourceActivity.java | 143 - .../style/BuildingFillExtrusionActivity.java | 151 - .../activity/style/CircleLayerActivity.java | 284 - .../activity/style/CustomSpriteActivity.java | 144 - .../style/DataDrivenStyleActivity.java | 472 - .../activity/style/DraggableMarkerActivity.kt | 331 - .../activity/style/FillExtrusionActivity.java | 122 - .../style/FillExtrusionStyleTestActivity.java | 78 - .../style/GeoJsonClusteringActivity.java | 281 - .../activity/style/GradientLineActivity.java | 117 - .../activity/style/GridSourceActivity.java | 154 - .../activity/style/HeatmapLayerActivity.java | 228 - .../style/HillshadeLayerActivity.java | 87 - .../testapp/activity/style/NoStyleActivity.kt | 84 - .../style/RealTimeGeoJsonActivity.java | 129 - .../activity/style/RuntimeStyleActivity.java | 607 - .../style/RuntimeStyleTimingTestActivity.java | 99 - .../activity/style/StyleFileActivity.java | 177 - .../style/SymbolGeneratorActivity.java | 373 - .../activity/style/SymbolLayerActivity.java | 375 - .../ZoomFunctionSymbolLayerActivity.java | 199 - .../PerformanceMeasurementActivity.java | 189 - .../TextureViewAnimationActivity.java | 148 - .../TextureViewDebugModeActivity.java | 26 - .../TextureViewResizeActivity.java | 103 - ...tureViewTransparentBackgroundActivity.java | 108 - .../turf/PhysicalUnitCircleActivity.kt | 125 - .../testapp/adapter/FeatureAdapter.java | 61 - .../adapter/FeatureSectionAdapter.java | 174 - .../testapp/model/activity/Feature.java | 69 - .../model/annotations/CityStateMarker.java | 18 - .../annotations/CityStateMarkerOptions.java | 68 - .../model/annotations/CountryMarker.java | 24 - .../annotations/CountryMarkerOptions.java | 75 - .../model/customlayer/ExampleCustomLayer.java | 19 - .../other/OfflineDownloadRegionDialog.java | 47 - .../model/other/OfflineListRegionsDialog.java | 36 - .../mapboxsdk/testapp/utils/FileUtils.kt | 64 - .../mapboxsdk/testapp/utils/FontCache.java | 26 - .../mapboxsdk/testapp/utils/GeoParseUtil.java | 51 - .../mapboxsdk/testapp/utils/IconUtils.java | 31 - .../testapp/utils/IdleZoomListener.java | 25 - .../testapp/utils/ItemClickSupport.java | 95 - .../mapboxsdk/testapp/utils/NavUtils.java | 14 - .../mapboxsdk/testapp/utils/OfflineUtils.java | 37 - .../testapp/utils/ResourceUtils.java | 39 - .../utils/TileLoadingMeasurementUtils.java | 217 - .../mapboxsdk/testapp/utils/TimberLogger.java | 57 - .../mapboxsdk/testapp/utils/TimingLogger.java | 160 - .../mapboxsdk/testapp/utils/TokenUtils.java | 37 - .../testapp/utils/ViewToBitmapUtil.java | 22 - .../view/LockableBottomSheetBehavior.java | 74 - .../mapboxsdk/testapp/view/MapViewPager.java | 20 - .../src/main/res/animator/scale_down.xml | 11 - .../src/main/res/animator/scale_up.xml | 11 - .../src/main/res/drawable-hdpi/ic_android.png | Bin 1711 -> 0 bytes .../main/res/drawable-hdpi/ic_android_2.png | Bin 1562 -> 0 bytes .../res/drawable-hdpi/ic_launcher_round.png | Bin 24697 -> 0 bytes .../src/main/res/drawable-hdpi/icon.png | Bin 19772 -> 0 bytes .../main/res/drawable-hdpi/icon_burned.png | Bin 1587 -> 0 bytes .../src/main/res/drawable-mdpi/ic_android.png | Bin 1217 -> 0 bytes .../main/res/drawable-mdpi/ic_android_2.png | Bin 1117 -> 0 bytes .../res/drawable-mdpi/ic_launcher_round.png | Bin 14115 -> 0 bytes .../src/main/res/drawable-mdpi/icon.png | Bin 11003 -> 0 bytes .../main/res/drawable-mdpi/icon_burned.png | Bin 913 -> 0 bytes .../res/drawable-mdpi/southeast_radar_0.png | Bin 172489 -> 0 bytes .../res/drawable-mdpi/southeast_radar_1.png | Bin 177163 -> 0 bytes .../res/drawable-mdpi/southeast_radar_2.png | Bin 179236 -> 0 bytes .../res/drawable-mdpi/southeast_radar_3.png | Bin 177074 -> 0 bytes .../main/res/drawable-xhdpi/ic_android.png | Bin 2202 -> 0 bytes .../main/res/drawable-xhdpi/ic_android_2.png | Bin 2142 -> 0 bytes .../res/drawable-xhdpi/ic_launcher_round.png | Bin 37126 -> 0 bytes .../src/main/res/drawable-xhdpi/icon.png | Bin 30669 -> 0 bytes .../main/res/drawable-xhdpi/icon_burned.png | Bin 1587 -> 0 bytes .../main/res/drawable-xxhdpi/ic_android.png | Bin 3639 -> 0 bytes .../main/res/drawable-xxhdpi/ic_android_2.png | Bin 3495 -> 0 bytes .../res/drawable-xxhdpi/ic_launcher_round.png | Bin 66853 -> 0 bytes .../src/main/res/drawable-xxhdpi/ic_us.png | Bin 418 -> 0 bytes .../src/main/res/drawable-xxhdpi/icon.png | Bin 58564 -> 0 bytes .../main/res/drawable-xxhdpi/icon_burned.png | Bin 2382 -> 0 bytes .../main/res/drawable-xxxhdpi/ic_android.png | Bin 4936 -> 0 bytes .../res/drawable-xxxhdpi/ic_android_2.png | Bin 4883 -> 0 bytes .../main/res/drawable-xxxhdpi/ic_car_top.png | Bin 19879 -> 0 bytes .../drawable-xxxhdpi/ic_launcher_round.png | Bin 103015 -> 0 bytes .../main/res/drawable-xxxhdpi/ic_taxi_top.png | Bin 11275 -> 0 bytes .../src/main/res/drawable-xxxhdpi/icon.png | Bin 93272 -> 0 bytes .../main/res/drawable-xxxhdpi/icon_burned.png | Bin 3018 -> 0 bytes .../src/main/res/drawable-xxxhdpi/water.jpg | Bin 165806 -> 0 bytes .../main/res/drawable/custom_user_arrow.xml | 11 - .../main/res/drawable/custom_user_icon.xml | 10 - .../res/drawable/custom_user_puck_icon.xml | 23 - .../src/main/res/drawable/ic_ab_back.xml | 12 - .../src/main/res/drawable/ic_add.xml | 9 - .../res/drawable/ic_add_a_photo_black.xml | 9 - .../src/main/res/drawable/ic_add_white.xml | 9 - .../drawable/ic_airplanemode_active_black.xml | 12 - .../res/drawable/ic_animate_coordinates.xml | 9 - .../main/res/drawable/ic_arrow_downward.xml | 9 - .../src/main/res/drawable/ic_arrow_upward.xml | 9 - .../src/main/res/drawable/ic_check_box.xml | 9 - .../src/main/res/drawable/ic_delete.xml | 9 - .../res/drawable/ic_directions_bus_black.xml | 9 - .../res/drawable/ic_directions_car_black.xml | 9 - .../res/drawable/ic_directions_run_black.xml | 9 - .../src/main/res/drawable/ic_droppin.xml | 4 - .../res/drawable/ic_hearing_black_24dp.xml | 9 - .../src/main/res/drawable/ic_input.xml | 9 - .../src/main/res/drawable/ic_layers.xml | 9 - .../src/main/res/drawable/ic_layers_clear.xml | 9 - .../main/res/drawable/ic_location_city.xml | 9 - .../res/drawable/ic_location_disabled.xml | 9 - .../src/main/res/drawable/ic_my_location.xml | 9 - .../src/main/res/drawable/ic_paint.xml | 9 - .../main/res/drawable/ic_pause_black_24dp.xml | 9 - .../res/drawable/ic_play_arrow_black_24dp.xml | 9 - .../src/main/res/drawable/ic_print.xml | 9 - .../src/main/res/drawable/ic_refresh.xml | 9 - .../src/main/res/drawable/ic_stars.xml | 9 - .../src/main/res/drawable/line_divider.xml | 12 - .../mapbox_user_icon_shadow_0px_test.xml | 19 - .../main/res/layout/activity_add_sprite.xml | 28 - .../layout/activity_animated_image_source.xml | 16 - .../res/layout/activity_animated_marker.xml | 17 - .../layout/activity_backstack_fragment.xml | 17 - .../main/res/layout/activity_bottom_sheet.xml | 76 - .../res/layout/activity_building_layer.xml | 35 - .../res/layout/activity_cache_management.xml | 32 - .../activity_camera_animation_types.xml | 51 - .../res/layout/activity_camera_animator.xml | 26 - .../res/layout/activity_camera_position.xml | 33 - .../main/res/layout/activity_camera_test.xml | 16 - .../activity_change_resources_cache_path.xml | 21 - .../main/res/layout/activity_circle_layer.xml | 42 - .../main/res/layout/activity_custom_layer.xml | 25 - .../res/layout/activity_data_driven_style.xml | 24 - .../main/res/layout/activity_debug_mode.xml | 90 - .../src/main/res/layout/activity_default.xml | 12 - .../res/layout/activity_draggable_marker.xml | 19 - .../res/layout/activity_dynamic_marker.xml | 29 - .../res/layout/activity_espresso_test.xml | 15 - .../res/layout/activity_extrusion_test.xml | 17 - .../res/layout/activity_feature_overview.xml | 14 - .../layout/activity_fill_extrusion_layer.xml | 18 - .../layout/activity_geojson_clustering.xml | 23 - .../res/layout/activity_gesture_detector.xml | 27 - .../res/layout/activity_gradient_line.xml | 10 - .../main/res/layout/activity_grid_source.xml | 16 - .../main/res/layout/activity_heatmaplayer.xml | 13 - .../res/layout/activity_hillshade_layer.xml | 16 - .../main/res/layout/activity_infowindow.xml | 17 - .../layout/activity_infowindow_adapter.xml | 17 - .../main/res/layout/activity_latlngbounds.xml | 47 - .../main/res/layout/activity_local_glyph.xml | 17 - ...vity_location_layer_activation_builder.xml | 17 - .../activity_location_layer_fragment.xml | 24 - .../activity_location_layer_map_change.xml | 30 - .../layout/activity_location_layer_mode.xml | 78 - .../activity_location_manual_update.xml | 43 - .../main/res/layout/activity_manual_zoom.xml | 18 - .../main/res/layout/activity_map_fragment.xml | 8 - .../res/layout/activity_map_in_dialog.xml | 11 - .../main/res/layout/activity_map_padding.xml | 33 - .../main/res/layout/activity_map_simple.xml | 14 - .../res/layout/activity_map_snapshotter.xml | 14 - .../activity_map_snapshotter_marker.xml | 14 - .../layout/activity_map_snapshotter_reuse.xml | 24 - .../res/layout/activity_map_visibility.xml | 17 - .../main/res/layout/activity_marker_bulk.xml | 17 - .../main/res/layout/activity_maxmin_zoom.xml | 16 - .../layout/activity_merge_offline_regions.xml | 20 - .../res/layout/activity_metadata_update.xml | 22 - .../main/res/layout/activity_multi_map.xml | 69 - .../src/main/res/layout/activity_offline.xml | 47 - .../layout/activity_offline_region_delete.xml | 21 - .../src/main/res/layout/activity_overlay.xml | 13 - .../res/layout/activity_physical_circle.xml | 32 - .../main/res/layout/activity_pixel_test.xml | 16 - .../src/main/res/layout/activity_polyline.xml | 29 - .../res/layout/activity_press_for_marker.xml | 20 - .../src/main/res/layout/activity_print.xml | 23 - .../layout/activity_query_features_box.xml | 25 - .../layout/activity_query_features_point.xml | 16 - .../layout/activity_query_source_features.xml | 26 - .../main/res/layout/activity_recyclerview.xml | 12 - .../res/layout/activity_region_download.xml | 216 - .../res/layout/activity_restricted_bounds.xml | 17 - .../res/layout/activity_runtime_style.xml | 12 - .../main/res/layout/activity_scroll_by.xml | 106 - .../src/main/res/layout/activity_snapshot.xml | 29 - .../main/res/layout/activity_style_file.xml | 38 - .../res/layout/activity_symbol_generator.xml | 13 - .../main/res/layout/activity_symbollayer.xml | 8 - .../layout/activity_textureview_animate.xml | 29 - .../activity_textureview_debug_mode.xml | 98 - .../layout/activity_textureview_resize.xml | 29 - .../activity_textureview_transparent.xml | 16 - .../main/res/layout/activity_viewpager.xml | 21 - .../res/layout/activity_zoom_symbol_layer.xml | 16 - .../res/layout/dialog_camera_position.xml | 212 - .../res/layout/drawer_navigation_drawer.xml | 7 - .../main/res/layout/fragment_dialog_map.xml | 16 - .../main/res/layout/fragment_double_map.xml | 22 - .../main/res/layout/item_gesture_alert.xml | 14 - .../src/main/res/layout/item_main_feature.xml | 38 - .../src/main/res/layout/item_map_gl.xml | 10 - .../src/main/res/layout/item_map_texture.xml | 11 - .../src/main/res/layout/item_viewpager.xml | 17 - .../main/res/layout/section_main_layout.xml | 29 - .../main/res/layout/view_custom_marker.xml | 22 - .../src/main/res/layout/view_text_marker.xml | 17 - .../src/main/res/menu/menu_animator.xml | 20 - .../src/main/res/menu/menu_bounds.xml | 16 - .../src/main/res/menu/menu_building.xml | 12 - .../src/main/res/menu/menu_bulk_marker.xml | 9 - .../src/main/res/menu/menu_custom_layer.xml | 15 - .../main/res/menu/menu_data_driven_style.xml | 50 - .../src/main/res/menu/menu_debug.xml | 17 - .../main/res/menu/menu_generator_symbol.xml | 12 - .../src/main/res/menu/menu_gestures.xml | 27 - .../src/main/res/menu/menu_infowindow.xml | 19 - .../src/main/res/menu/menu_location_mode.xml | 39 - .../src/main/res/menu/menu_padding.xml | 8 - .../src/main/res/menu/menu_polygon.xml | 24 - .../src/main/res/menu/menu_polyline.xml | 25 - .../main/res/menu/menu_press_for_marker.xml | 8 - .../src/main/res/menu/menu_runtime_style.xml | 73 - .../src/main/res/menu/menu_symbol_layer.xml | 16 - .../src/main/res/menu/menu_symbols.xml | 12 - .../src/main/res/menu/menu_zoom.xml | 25 - .../src/main/res/raw/amsterdam.geojson | 2283 - .../src/main/res/raw/local_style.json | 7660 - .../src/main/res/raw/no_bg_style.json | 43 - .../src/main/res/raw/sat_style.json | 38 - .../res/raw/test_feature_collection.geojson | 26 - .../res/raw/test_feature_properties.geojson | 21 - .../raw/test_line_gradient_feature.geojson | 38 - .../res/raw/test_line_string_feature.geojson | 17 - .../test_multi_line_string_feature.geojson | 29 - .../res/raw/test_multi_point_feature.geojson | 17 - .../raw/test_multi_polygon_feature.geojson | 49 - .../main/res/raw/test_point_feature.geojson | 11 - .../main/res/raw/test_points_utrecht.geojson | 49 - .../main/res/raw/test_polygon_feature.geojson | 27 - .../test_polygon_with_hole_feature.geojson | 45 - .../src/main/res/raw/tiny_countries.geojson | 2741 - .../src/main/res/values-v21/attrs.xml | 6 - .../src/main/res/values-v21/styles.xml | 19 - .../src/main/res/values/actions.xml | 94 - .../src/main/res/values/arrays.xml | 15 - .../src/main/res/values/categories.xml | 21 - .../src/main/res/values/colors.xml | 13 - .../src/main/res/values/descriptions.xml | 89 - .../src/main/res/values/dimens.xml | 10 - .../src/main/res/values/ids.xml | 11 - .../src/main/res/values/strings.xml | 6 - .../src/main/res/values/styles.xml | 57 - .../src/main/res/values/titles.xml | 89 - platform/android/TESTS.md | 117 - .../android/art/tests/FirebaseTestsAS_1.png | Bin 53000 -> 0 bytes .../android/art/tests/FirebaseTestsAS_2.png | Bin 93449 -> 0 bytes .../android/art/tests/FirebaseTestsAS_3-5.png | Bin 219290 -> 0 bytes .../android/art/tests/FirebaseTestsAS_6.png | Bin 154418 -> 0 bytes .../android/art/tests/FirebaseTestsAS_7.png | Bin 221809 -> 0 bytes platform/android/build.gradle | 38 - platform/android/checkstyle.xml | 226 - platform/android/config.cmake | 143 - platform/android/core-files.json | 195 - platform/android/filesource-files.json | 27 - .../android/gradle/android-nitpick.gradle | 66 - .../android/gradle/artifact-settings.gradle | 21 - platform/android/gradle/dependencies.gradle | 90 - platform/android/gradle/gradle-bintray.gradle | 117 - .../android/gradle/gradle-checkstyle.gradle | 26 - platform/android/gradle/gradle-config.gradle | 23 - .../gradle/gradle-dependencies-graph.gradle | 29 - platform/android/gradle/gradle-javadoc.gradle | 18 - platform/android/gradle/gradle-lint.gradle | 30 - platform/android/gradle/gradle-make.gradle | 18 - .../gradle-update-vendor-modules.gradle | 8 - platform/android/gradle/jacoco-report.gradle | 33 - platform/android/gradle/ktlint.gradle | 27 - platform/android/gradle/native-build.gradle | 70 - .../android/gradle/wrapper/gradle-wrapper.jar | Bin 54227 -> 0 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 - platform/android/gradlew | 172 - platform/android/gradlew.bat | 84 - platform/android/resources/compass.svg | 34 - platform/android/resources/direction-dot.svg | 28 - platform/android/resources/dot-arrow.svg | 11 - platform/android/resources/location-dot.svg | 15 - .../android/resources/location-static-dot.svg | 15 - .../android/scripts/exclude-activity-gen.json | 53 - platform/android/scripts/generate-license.py | 39 - .../android/scripts/generate-style-code.js | 400 - .../android/scripts/generate-test-code.js | 73 - platform/android/scripts/metrics.sh | 19 - .../android/scripts/parse-jacoco-report.py | 31 - platform/android/scripts/release.py | 236 - platform/android/scripts/run-render-test.py | 43 - platform/android/scripts/validate-license.py | 17 - platform/android/settings.gradle | 1 - platform/android/tests/README.md | 10 - .../tests/docs/ACTIVITY_SANITY_TEST.md | 17 - platform/android/tests/docs/CORE_TESTS.md | 21 - .../android/tests/docs/EXERCISER_TESTS.md | 23 - .../android/tests/docs/PERFORMANCE_TESTS.md | 69 - platform/android/tests/docs/UI_TESTS.md | 67 - platform/android/tests/docs/UNIT_TESTS.md | 92 - platform/android/tests/skipped.txt | 9 - platform/android/vendor/mapbox-events-android | 1 - .../android/vendor/mapbox-gestures-android | 1 - platform/android/vendor/mapbox-java | 1 - platform/android/version-script | 4 - 885 files changed, 4 insertions(+), 387685 deletions(-) delete mode 100644 platform/android/.editorconfig delete mode 100644 platform/android/.project delete mode 100644 platform/android/.settings/org.eclipse.buildship.core.prefs delete mode 100644 platform/android/DISTRIBUTE.md delete mode 100644 platform/android/LICENSE.md delete mode 100644 platform/android/MapboxGLAndroidSDK/.gitignore delete mode 100644 platform/android/MapboxGLAndroidSDK/build.gradle delete mode 100644 platform/android/MapboxGLAndroidSDK/gradle.properties delete mode 100644 platform/android/MapboxGLAndroidSDK/lint.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/proguard-rules.pro delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/AccountsManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoaderProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictMode.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictModeException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProviderImpl.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BasePointCollection.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolygonOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolylineOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/TelemetryConstants.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/CalledFromWorkerThreadException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpIdentifier.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpLogger.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpResponder.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/LocalRequestTask.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/NativeHttpRequest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/AnimatorListenerHolder.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentNotInitializedException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationClickListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationLongClickListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnRenderModeChangedListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/Logger.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/LoggerDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/SupportMapFragment.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/TelemetryDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Transform.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/UiSettings.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRenderer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererRunnable.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/MapRendererScheduler.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigChooser.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/EGLConfigException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/egl/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/GLSurfaceViewMapRenderer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/MapboxGLSurfaceView.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/glsurfaceview/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewMapRenderer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/TextureViewRenderThread.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/renderer/textureview/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/CompassView.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/widgets/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/http/HttpRequestImpl.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/http/HttpRequestUtil.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/http/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/loader/LibraryLoaderProviderImpl.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/loader/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/MapBaseEvent.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/MapEventFactory.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/MapLoadEvent.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/OfflineDownloadEndEvent.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/OfflineDownloadStartEvent.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/PerformanceEvent.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/PhoneState.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/TelemetryImpl.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/module/telemetry/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/ConnectivityReceiver.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/NativeConnectivityListener.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/net/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineGeometryRegionDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineManager.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionError.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineRegionStatus.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/OfflineTilePyramidRegionDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/offline/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnaphotUtil.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshot.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/FileSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/Resource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/storage/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/Expression.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/expressions/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CannotAddLayerException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillExtrusionLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HeatmapLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/HillshadeLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutPropertyValue.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintPropertyValue.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyValue.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/TransitionOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Position.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CannotAddSourceException.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/CustomGeometrySourceOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeometryTileProvider.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/ImageSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterDemSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/TileSet.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/UnknownSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/types/Formatted.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/types/FormattedSection.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/types/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/AnimatorUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/BitmapUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ColorUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/Compare.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/FileUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/FontUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MapFragmentUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/MathUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/StringUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/ThreadUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/utils/package-info.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/overview.html delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res-public/values/public.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_compass_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_helmet.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_logo_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_marker_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_markerview_icon_default.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_mylocation_icon_bearing.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-hdpi/mapbox_mylocation_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_compass_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_helmet.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_logo_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_marker_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_markerview_icon_default.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_mylocation_icon_bearing.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-mdpi/mapbox_mylocation_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_compass_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_helmet.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_logo_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_marker_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_markerview_icon_default.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_mylocation_icon_bearing.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xhdpi/mapbox_mylocation_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_compass_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_helmet.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_logo_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_marker_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_markerview_icon_default.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mylocation_icon_bearing.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxhdpi/mapbox_mylocation_icon_default.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_compass_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_helmet.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_logo_icon.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_marker_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_markerview_icon_default.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_mylocation_icon_bearing.png delete mode 100755 platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/mapbox_mylocation_icon_default.png delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_bg_selector.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_icon_default.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_info_icon_selected.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_mylocation_bg_shape.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_popup_window_transparent.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_rounded_corner.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_user_bearing_icon.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_user_icon.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_user_icon_shadow.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_user_icon_stale.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_user_puck_icon.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/drawable/mapbox_user_stroke_icon.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_attribution_list_item.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_infowindow_content.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_mapview_internal.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/layout/mapbox_view_image_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-bg/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-ca/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-cs/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-es/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-fr/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-gl/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-he/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-hu/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-iw/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-ko/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-lt/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-nl/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-pl/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-pt-rPT/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-ru/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-sv/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-uk/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-vi/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-zh-rCN/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-zh-rHK/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values-zh-rTW/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values/attrs.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values/colors.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values/dimens.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/main/res/values/styles.xml delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/AccountsManagerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxInjector.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptionsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManagerTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/log/LoggerTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AttributionDialogManagerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleBuilderTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/StyleTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/TransformTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/UiSettingsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUrlTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/http/HttpRequestUtilTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/telemetry/MapEventFactoryTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/module/telemetry/SchemaTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/style/expressions/ExpressionTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/telemetry/HttpTransportTest.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/ColorUtilsTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/utils/MockParcel.java delete mode 100644 platform/android/MapboxGLAndroidSDK/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker delete mode 100644 platform/android/MapboxGLAndroidSDKLint/.gitignore delete mode 100644 platform/android/MapboxGLAndroidSDKLint/build.gradle delete mode 100644 platform/android/MapboxGLAndroidSDKLint/src/main/java/com/mapbox/mapboxsdk/lint/KeepDetector.kt delete mode 100644 platform/android/MapboxGLAndroidSDKLint/src/main/java/com/mapbox/mapboxsdk/lint/MapboxIssueRegistry.kt delete mode 100644 platform/android/MapboxGLAndroidSDKLint/src/test/java/com/mapbox/mapboxsdk/lint/KeepDetectorTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/.gitignore delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/README.md delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/build.gradle delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/lint-baseline-local.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/lint/lint-baseline-ci.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/proguard-rules.pro delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/AndroidManifest.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/assets/streets.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/AppCenter.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/InstrumentationApplication.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/InstrumentationRunner.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactoryTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/BaseIntegrationTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/FragmentBackStackTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/GLSurfaceViewReopenTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/GLSurfaceViewReuseTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/OrientationChangeTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/QueryRenderedFeaturesBoxCountTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/TextureViewReopenTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/TextureViewReuseTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/integration/ViewPagerScrollTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/LocationComponentAction.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/MapboxLocationTestingUtils.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/location/utils/StyleChangeIdlingResource.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/BaseLayerTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/GesturesUiTestUtils.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/IconManagerResolver.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapGestureDetectorTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/MapboxTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/NativeMapViewTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/TransformTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/offline/OfflineDownloadTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotterTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/MapboxMapAction.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/OrientationAction.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/action/WaitAction.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/BaseTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/EspressoTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/activity.junit.ejs delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/IconTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/MarkerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolygonTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/annotations/PolylineTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraAnimateTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraEaseTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraForTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraMoveTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/camera/CameraTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/fragment/MapDialogFragmentTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/GeoJsonConversionTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/geometry/LatLngBoundsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/ImageMissingTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/RemoveUnusedImagesTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/StyleLoadTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/AttributionTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/CompassViewTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/maps/widgets/LogoTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/CacheTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineManagerTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/offline/OfflineUtilsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/render/RenderTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceMapTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceStandaloneTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/storage/FileSourceTestUtils.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/string/UppperLowerCaseTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/BackgroundLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CustomGeometrySourceTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ExpressionTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillExtrusionLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/FillLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/GeoJsonSourceTests.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HeatmapLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/HillshadeLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/ImageTest.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RasterLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTests.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/RuntimeStyleTimingTests.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/StyleLoaderTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/SnapshotterIdlingResource.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestConstants.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/utils/TestingAsyncUtils.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/utils/FontUtilsTest.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Medium.ttf delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/Roboto-Regular.ttf delete mode 100755 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/commercial-24.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/dog-park-24.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/earthquakes.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fill_filter_style.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Bold,Arial Unicode MS Bold/0-255.pbf delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Italic,Arial Unicode MS Regular/0-255.pbf delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Medium,Arial Unicode MS Regular/0-255.pbf delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/0-255.pbf delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/fonts/DIN Offc Pro Regular,Arial Unicode MS Regular/8192-8447.pbf delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/heavy_style.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/line_filter_style.json delete mode 100755 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/london-underground-24.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/numeric_filter_style.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/offline_test.db delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/points-sf.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/points.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/small_line.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/small_poly.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/streets.json delete mode 100755 platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/town-hall-24.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/MapboxApplication.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/FeatureOverviewActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/AnimatedSymbolLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/BulkMarkerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/DynamicMarkerChangeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolygonActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PolylineActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/PressForMarkerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimationTypeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraAnimatorActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/CameraPositionActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/GestureDetectorActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/LatLngBoundsActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ManualZoomActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/MaxMinZoomActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/camera/ScrollByActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/espresso/DeviceIndependentTestActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/espresso/EspressoTestActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/espresso/PixelTestActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxSymbolCountActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QuerySourceFeaturesActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/FragmentBackStackActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MapFragmentActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/MultiMapActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/NestedViewPagerActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/SupportMapFragmentActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/fragment/ViewPagerActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/PrintActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/imagegenerator/SnapshotActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/DynamicInfoWindowAdapterActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/infowindow/InfoWindowAdapterActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationComponentActivationActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationFragmentActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationMapChangeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/LocationModesActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/ManualLocationUpdatesActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/location/Utils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/BottomSheetActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DebugModeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/DoubleMapActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/GLSurfaceRecyclerViewActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LatLngBoundsForCameraActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/LocalGlyphActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapInDialogActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapPaddingActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/OverlayMapActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/SimpleMapActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/TextureRecyclerViewActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/VisibilityChangeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/ChangeResourcesCachePathActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DeleteRegionActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/DownloadRegionActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/MergeOfflineRegionsActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/OfflineActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/offline/UpdateMetadataActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestSnapshotter.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestStyleDefinition.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/CacheManagementActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/storage/UrlTransformActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/AnimatedImageSourceActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/BuildingFillExtrusionActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CircleLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/CustomSpriteActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DataDrivenStyleActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/DraggableMarkerActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/FillExtrusionStyleTestActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GradientLineActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GridSourceActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HeatmapLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/HillshadeLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/NoStyleActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RealTimeGeoJsonActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleTimingTestActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/StyleFileActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolGeneratorActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/SymbolLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/ZoomFunctionSymbolLayerActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/telemetry/PerformanceMeasurementActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewAnimationActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewDebugModeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewResizeActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/textureview/TextureViewTransparentBackgroundActivity.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/turf/PhysicalUnitCircleActivity.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureAdapter.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/adapter/FeatureSectionAdapter.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/activity/Feature.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarker.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CityStateMarkerOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarker.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/annotations/CountryMarkerOptions.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineDownloadRegionDialog.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/model/other/OfflineListRegionsDialog.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FileUtils.kt delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/FontCache.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/GeoParseUtil.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/IconUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/IdleZoomListener.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ItemClickSupport.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/NavUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/OfflineUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ResourceUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TileLoadingMeasurementUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimberLogger.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TimingLogger.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/TokenUtils.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/utils/ViewToBitmapUtil.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/LockableBottomSheetBehavior.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/view/MapViewPager.java delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_down.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/animator/scale_up.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_android.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_android_2.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/ic_launcher_round.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-hdpi/icon_burned.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_android.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_android_2.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/ic_launcher_round.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/icon_burned.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_0.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_1.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_2.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-mdpi/southeast_radar_3.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_android.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_android_2.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/ic_launcher_round.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xhdpi/icon_burned.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_android.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_android_2.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_launcher_round.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/ic_us.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxhdpi/icon_burned.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_android.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_android_2.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_car_top.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_launcher_round.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/ic_taxi_top.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/icon_burned.png delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable-xxxhdpi/water.jpg delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/custom_user_arrow.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/custom_user_icon.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/custom_user_puck_icon.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_ab_back.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_a_photo_black.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_add_white.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_airplanemode_active_black.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_animate_coordinates.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_downward.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_arrow_upward.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_check_box.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_delete.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_bus_black.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_car_black.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_directions_run_black.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_droppin.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_hearing_black_24dp.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_input.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_layers_clear.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_city.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_location_disabled.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_my_location.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_paint.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_pause_black_24dp.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_play_arrow_black_24dp.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_print.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_refresh.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/ic_stars.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/line_divider.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/drawable/mapbox_user_icon_shadow_0px_test.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_add_sprite.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_image_source.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_animated_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_backstack_fragment.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_bottom_sheet.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_building_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_cache_management.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animation_types.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_animator.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_position.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_camera_test.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_change_resources_cache_path.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_circle_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_custom_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_data_driven_style.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_debug_mode.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_default.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_draggable_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_dynamic_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_espresso_test.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_extrusion_test.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_feature_overview.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_fill_extrusion_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_geojson_clustering.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gesture_detector.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_gradient_line.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_grid_source.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_heatmaplayer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_hillshade_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_infowindow_adapter.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_latlngbounds.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_local_glyph.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_layer_activation_builder.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_layer_fragment.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_layer_map_change.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_layer_mode.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_location_manual_update.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_manual_zoom.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_fragment.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_in_dialog.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_padding.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_simple.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_snapshotter_reuse.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_map_visibility.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_marker_bulk.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_maxmin_zoom.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_merge_offline_regions.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_metadata_update.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_multi_map.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_offline_region_delete.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_overlay.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_physical_circle.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_pixel_test.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_polyline.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_press_for_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_print.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_box.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_point.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_source_features.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_recyclerview.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_region_download.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_restricted_bounds.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_scroll_by.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_snapshot.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_style_file.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbol_generator.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_symbollayer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_animate.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_debug_mode.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_resize.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_textureview_transparent.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_viewpager.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_zoom_symbol_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/dialog_camera_position.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/drawer_navigation_drawer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_dialog_map.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/fragment_double_map.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_gesture_alert.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_main_feature.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_map_gl.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_map_texture.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/item_viewpager.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/section_main_layout.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_custom_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/view_text_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_animator.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bounds.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_building.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_bulk_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_custom_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_data_driven_style.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_debug.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_generator_symbol.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_gestures.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_infowindow.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_location_mode.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_padding.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polygon.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_polyline.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_press_for_marker.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbol_layer.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_symbols.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_zoom.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/local_style.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/no_bg_style.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/sat_style.json delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_feature_collection.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_feature_properties.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_line_gradient_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_line_string_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_multi_line_string_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_multi_point_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_multi_polygon_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_point_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_points_utrecht.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_polygon_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/test_polygon_with_hole_feature.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/tiny_countries.geojson delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/attrs.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values-v21/styles.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml delete mode 100644 platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml delete mode 100644 platform/android/TESTS.md delete mode 100644 platform/android/art/tests/FirebaseTestsAS_1.png delete mode 100644 platform/android/art/tests/FirebaseTestsAS_2.png delete mode 100644 platform/android/art/tests/FirebaseTestsAS_3-5.png delete mode 100644 platform/android/art/tests/FirebaseTestsAS_6.png delete mode 100644 platform/android/art/tests/FirebaseTestsAS_7.png delete mode 100644 platform/android/build.gradle delete mode 100644 platform/android/checkstyle.xml delete mode 100644 platform/android/config.cmake delete mode 100644 platform/android/core-files.json delete mode 100644 platform/android/filesource-files.json delete mode 100644 platform/android/gradle/android-nitpick.gradle delete mode 100644 platform/android/gradle/artifact-settings.gradle delete mode 100644 platform/android/gradle/dependencies.gradle delete mode 100644 platform/android/gradle/gradle-bintray.gradle delete mode 100644 platform/android/gradle/gradle-checkstyle.gradle delete mode 100644 platform/android/gradle/gradle-config.gradle delete mode 100644 platform/android/gradle/gradle-dependencies-graph.gradle delete mode 100644 platform/android/gradle/gradle-javadoc.gradle delete mode 100644 platform/android/gradle/gradle-lint.gradle delete mode 100644 platform/android/gradle/gradle-make.gradle delete mode 100644 platform/android/gradle/gradle-update-vendor-modules.gradle delete mode 100644 platform/android/gradle/jacoco-report.gradle delete mode 100644 platform/android/gradle/ktlint.gradle delete mode 100644 platform/android/gradle/native-build.gradle delete mode 100644 platform/android/gradle/wrapper/gradle-wrapper.jar delete mode 100644 platform/android/gradle/wrapper/gradle-wrapper.properties delete mode 100755 platform/android/gradlew delete mode 100644 platform/android/gradlew.bat delete mode 100755 platform/android/resources/compass.svg delete mode 100755 platform/android/resources/direction-dot.svg delete mode 100644 platform/android/resources/dot-arrow.svg delete mode 100755 platform/android/resources/location-dot.svg delete mode 100755 platform/android/resources/location-static-dot.svg delete mode 100644 platform/android/scripts/exclude-activity-gen.json delete mode 100644 platform/android/scripts/generate-license.py delete mode 100755 platform/android/scripts/generate-style-code.js delete mode 100644 platform/android/scripts/generate-test-code.js delete mode 100755 platform/android/scripts/metrics.sh delete mode 100755 platform/android/scripts/parse-jacoco-report.py delete mode 100644 platform/android/scripts/release.py delete mode 100644 platform/android/scripts/run-render-test.py delete mode 100644 platform/android/scripts/validate-license.py delete mode 100644 platform/android/settings.gradle delete mode 100644 platform/android/tests/README.md delete mode 100644 platform/android/tests/docs/ACTIVITY_SANITY_TEST.md delete mode 100644 platform/android/tests/docs/CORE_TESTS.md delete mode 100644 platform/android/tests/docs/EXERCISER_TESTS.md delete mode 100644 platform/android/tests/docs/PERFORMANCE_TESTS.md delete mode 100644 platform/android/tests/docs/UI_TESTS.md delete mode 100644 platform/android/tests/docs/UNIT_TESTS.md delete mode 100644 platform/android/tests/skipped.txt delete mode 160000 platform/android/vendor/mapbox-events-android delete mode 160000 platform/android/vendor/mapbox-gestures-android delete mode 160000 platform/android/vendor/mapbox-java delete mode 100644 platform/android/version-script diff --git a/Makefile b/Makefile index 1c1199932..170ff866c 100644 --- a/Makefile +++ b/Makefile @@ -461,309 +461,6 @@ test-node: node npm run test-query npm run test-memory -#### Android targets ########################################################### - -MBGL_ANDROID_ABIS = arm-v7;armeabi-v7a -MBGL_ANDROID_ABIS += arm-v8;arm64-v8a -MBGL_ANDROID_ABIS += x86;x86 -MBGL_ANDROID_ABIS += x86-64;x86_64 - -MBGL_ANDROID_LOCAL_WORK_DIR = /data/local/tmp/core-tests -MBGL_ANDROID_LOCAL_BENCHMARK_DIR = /data/local/tmp/benchmark -MBGL_ANDROID_LIBDIR = lib$(if $(filter arm-v8 x86-64,$1),64) -MBGL_ANDROID_DALVIKVM = dalvikvm$(if $(filter arm-v8 x86-64,$1),64,32) -MBGL_ANDROID_APK_SUFFIX = $(if $(filter Release,$(BUILDTYPE)),release,debug) -MBGL_ANDROID_CORE_TEST_DIR = platform/android/MapboxGLAndroidSDK/.externalNativeBuild/cmake/$(buildtype)/$2/core-tests -MBGL_ANDROID_BENCHMARK_DIR = platform/android/MapboxGLAndroidSDK/.externalNativeBuild/cmake/$(buildtype)/$2/benchmark -MBGL_ANDROID_STL ?= c++_static -MBGL_ANDROID_GRADLE = ./gradlew --parallel --max-workers=$(JOBS) -Pmapbox.buildtype=$(buildtype) -Pmapbox.stl=$(MBGL_ANDROID_STL) - -# Lists all devices, and extracts the identifiers, then obtains the ABI for every one. -# Some devices return \r\n, so we'll have to remove the carriage return before concatenating. -MBGL_ANDROID_ACTIVE_ARCHS = $(shell adb devices | sed '1d;/^\*/d;s/[[:space:]].*//' | xargs -n 1 -I DEV `type -P adb` -s DEV shell getprop ro.product.cpu.abi | tr -d '\r') - -# Generate code based on the style specification -.PHONY: android-style-code -android-style-code: - node platform/android/scripts/generate-style-code.js -style-code: android-style-code - -# Configuration file for running CMake from Gradle within Android Studio. -platform/android/gradle/configuration.gradle: - @printf "ext {\n node = '`command -v node || command -v nodejs`'\n npm = '`command -v npm`'\n ccache = '`command -v ccache`'\n}" > $@ - -define ANDROID_RULES -# $1 = arm-v7 (short arch) -# $2 = armeabi-v7a (internal arch) - -.PHONY: android-test-lib-$1 -android-test-lib-$1: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 -Pmapbox.with_test=true :MapboxGLAndroidSDKTestApp:assemble$(BUILDTYPE) - -.PHONY: android-benchmark-$1 -android-benchmark-$1: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 -Pmapbox.with_benchmark=true :MapboxGLAndroidSDKTestApp:assemble$(BUILDTYPE) - -# Build SDK for for specified abi -.PHONY: android-lib-$1 -android-lib-$1: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDK:assemble$(BUILDTYPE) - -# Build test app and SDK for for specified abi -.PHONY: android-$1 -android-$1: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:assemble$(BUILDTYPE) - -# Build the core test for specified abi -.PHONY: android-core-test-$1 -android-core-test-$1: android-test-lib-$1 - # Compile main sources and extract the classes (using the test app to get all transitive dependencies in one place) - mkdir -p $(MBGL_ANDROID_CORE_TEST_DIR) - unzip -o platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/$(buildtype)/MapboxGLAndroidSDKTestApp-$(MBGL_ANDROID_APK_SUFFIX).apk classes.dex -d $(MBGL_ANDROID_CORE_TEST_DIR) - -run-android-core-test-$1-%: android-core-test-$1 - # Ensure clean state on the device - adb shell "rm -Rf $(MBGL_ANDROID_LOCAL_WORK_DIR) && mkdir -p $(MBGL_ANDROID_LOCAL_WORK_DIR)/test && mkdir -p $(MBGL_ANDROID_LOCAL_WORK_DIR)/mapbox-gl-js/src/style-spec/reference" - - # Push all needed files to the device - adb push $(MBGL_ANDROID_CORE_TEST_DIR)/classes.dex $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1 - adb push platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/$(buildtype)/jni/$2/libmapbox-gl.so $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1 - adb push test/fixtures $(MBGL_ANDROID_LOCAL_WORK_DIR)/test > /dev/null 2>&1 - adb push mapbox-gl-js/src/style-spec/reference/v8.json $(MBGL_ANDROID_LOCAL_WORK_DIR)/mapbox-gl-js/src/style-spec/reference > /dev/null 2>&1 - adb push platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/$(buildtype)/obj/$2/mbgl-test $(MBGL_ANDROID_LOCAL_WORK_DIR) > /dev/null 2>&1 - -# Create gtest filter for skipped tests. - $(eval SKIPPED_TESTS := -$(shell sed -n '/#\|^$$/!p' platform/android/tests/skipped.txt | sed ':a;$!N;s/\n/:/g;ta')) - - # Kick off the tests - adb shell "export LD_LIBRARY_PATH=$(MBGL_ANDROID_LOCAL_WORK_DIR) && cd $(MBGL_ANDROID_LOCAL_WORK_DIR) && chmod +x mbgl-test && ./mbgl-test --class_path=$(MBGL_ANDROID_LOCAL_WORK_DIR)/classes.dex --gtest_filter=$$*:$(SKIPPED_TESTS)" - - # Gather the results and unpack them - adb shell "cd $(MBGL_ANDROID_LOCAL_WORK_DIR) && tar -cvzf results.tgz test/fixtures/* > /dev/null 2>&1" - adb pull $(MBGL_ANDROID_LOCAL_WORK_DIR)/results.tgz $(MBGL_ANDROID_CORE_TEST_DIR)/ > /dev/null 2>&1 - rm -rf $(MBGL_ANDROID_CORE_TEST_DIR)/results && mkdir -p $(MBGL_ANDROID_CORE_TEST_DIR)/results - tar -xzf $(MBGL_ANDROID_CORE_TEST_DIR)/results.tgz --strip-components=2 -C $(MBGL_ANDROID_CORE_TEST_DIR)/results - -# Run the core test for specified abi -.PHONY: run-android-core-test-$1 -run-android-core-test-$1: run-android-core-test-$1-* - -# Run benchmarks for specified abi -.PHONY: run-android-benchmark-$1 -run-android-benchmark-$1: run-android-benchmark-$1-* - -run-android-benchmark-$1-%: android-benchmark-$1 - mkdir -p $(MBGL_ANDROID_BENCHMARK_DIR) - unzip -o platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/$(buildtype)/MapboxGLAndroidSDKTestApp-$(MBGL_ANDROID_APK_SUFFIX).apk classes.dex -d $(MBGL_ANDROID_BENCHMARK_DIR) - - # Delete old test folder and create new one - adb shell "rm -Rf $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR) && mkdir -p $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR)/benchmark && mkdir -p $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR)/test" - - # Push compiled java sources, test data and executable to device - adb push $(MBGL_ANDROID_BENCHMARK_DIR)/classes.dex $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR) > /dev/null 2>&1 - adb push platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/$(buildtype)/jni/$2/libmapbox-gl.so $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR) > /dev/null 2>&1 - adb push benchmark/fixtures $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR)/benchmark > /dev/null 2>&1 - adb push test/fixtures $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR)/test > /dev/null 2>&1 - adb push platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/$(buildtype)/obj/$2/mbgl-benchmark $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR) > /dev/null 2>&1 - - # Run benchmark. Number of benchmark iterations can be set by run-android-benchmark-N parameter. - adb shell "export LD_LIBRARY_PATH=$(MBGL_ANDROID_LOCAL_BENCHMARK_DIR) && cd $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR) && chmod +x mbgl-benchmark && ./mbgl-benchmark --class_path=$(MBGL_ANDROID_LOCAL_BENCHMARK_DIR)/classes.dex --benchmark_repetitions=$$* --benchmark_format=json --benchmark_out=results.json" - - # Pull results.json from the device - rm -rf $(MBGL_ANDROID_BENCHMARK_DIR)/results && mkdir -p $(MBGL_ANDROID_BENCHMARK_DIR)/results - adb pull $(MBGL_ANDROID_LOCAL_BENCHMARK_DIR)/results.json $(MBGL_ANDROID_BENCHMARK_DIR)/results > /dev/null 2>&1 - -# Run the test app on connected android device with specified abi -.PHONY: run-android-$1 -run-android-$1: platform/android/gradle/configuration.gradle - -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:install$(BUILDTYPE) && adb shell am start -n com.mapbox.mapboxsdk.testapp/.activity.FeatureOverviewActivity - -# Build test app instrumentation tests apk and test app apk for specified abi -.PHONY: android-ui-test-$1 -android-ui-test-$1: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:assembleDebug :MapboxGLAndroidSDKTestApp:assembleAndroidTest - -# Run test app instrumentation tests on a connected android device or emulator with specified abi -.PHONY: run-android-ui-test-$1 -run-android-ui-test-$1: platform/android/gradle/configuration.gradle - -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest - -# Run Java Instrumentation tests on a connected android device or emulator with specified abi and test filter -run-android-ui-test-$1-%: platform/android/gradle/configuration.gradle - -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="$$*" - -# Symbolicate native stack trace with the specified abi -.PHONY: android-ndk-stack-$1 -android-ndk-stack-$1: platform/android/gradle/configuration.gradle - adb logcat | ndk-stack -sym platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj/$2/ - -# Run render tests with pixelmatch -.PHONY: run-android-render-test-$1 -run-android-render-test-$1: $(BUILD_DEPS) platform/android/gradle/configuration.gradle - -adb uninstall com.mapbox.mapboxsdk.testapp 2> /dev/null - # delete old test results - rm -rf platform/android/build/render-test/mapbox/ - # copy test definitions & ignore file to test app assets folder, clear old ones first - rm -rf platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/integration - cp -r mapbox-gl-js/test/integration platform/android/MapboxGLAndroidSDKTestApp/src/main/assets - cp platform/node/test/ignores.json platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/integration/ignores.json - # run RenderTest.java to generate static map images - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=$2 :MapboxGLAndroidSDKTestApp:connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class="com.mapbox.mapboxsdk.testapp.render.RenderTest" - # pull generated images from the device - adb pull "`adb shell 'printenv EXTERNAL_STORAGE' | tr -d '\r'`/mapbox/render" platform/android/build/render-test - # copy expected result and run pixelmatch - python platform/android/scripts/run-render-test.py - # remove test definitions from assets - rm -rf platform/android/MapboxGLAndroidSDKTestApp/src/main/assets/integration - -endef - -# Explodes the arguments into individual variables -define ANDROID_RULES_INVOKER -$(call ANDROID_RULES,$(word 1,$1),$(word 2,$1)) -endef - -$(foreach abi,$(MBGL_ANDROID_ABIS),$(eval $(call ANDROID_RULES_INVOKER,$(subst ;, ,$(abi))))) - -# Build the Android SDK and test app with abi set to arm-v7 -.PHONY: android -android: android-arm-v7 - -# Build the Android SDK with abi set to arm-v7 -.PHONY: android-lib -android-lib: android-lib-arm-v7 - -# Run the test app on connected android device with abi set to arm-v7 -.PHONY: run-android -run-android: run-android-arm-v7 - -# Run Java Instrumentation tests on a connected android device or emulator with abi set to arm-v7 -.PHONY: run-android-ui-test -run-android-ui-test: run-android-ui-test-arm-v7 -run-android-ui-test-%: run-android-ui-test-arm-v7-% - -# Run Java Unit tests on the JVM of the development machine executing this -.PHONY: run-android-unit-test -run-android-unit-test: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest --info -run-android-unit-test-%: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:testDebugUnitTest --info --tests "$*" - -# Run unit test and build a coverage report from .exec file generated by unit tests and .ec file generated by instrumentation tests -.PHONY: android-create-jacoco-report -android-create-jacoco-report: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:jacocoTestReport - -# Parse merged jacoco report and send it to S3 -.PHONY: android-parse-and-send-jacoco-report -android-parse-and-send-jacoco-report: - python platform/android/scripts/parse-jacoco-report.py - -# Builds a release package of the Android SDK -.PHONY: apackage -apackage: platform/android/gradle/configuration.gradle - make android-lib-arm-v7 && make android-lib-arm-v8 && make android-lib-x86 && make android-lib-x86-64 - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all assemble$(BUILDTYPE) - -# Build test app instrumentation tests apk and test app apk for all abi's -.PHONY: android-ui-test -android-ui-test: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDKTestApp:assembleDebug :MapboxGLAndroidSDKTestApp:assembleAndroidTest - -#Run instrumentations tests on MicroSoft App Center -.PHONY: run-android-test-app-center -run-android-test-app-center: - cd platform/android && appcenter test run espresso --app "mapboxcn-outlook.com/MapsSdk" --devices "mapboxcn-outlook.com/china" --app-path MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk --test-series "master" --locale "en_US" --build-dir MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug - -# Uploads the compiled Android SDK to Bintray -.PHONY: run-android-upload-to-bintray -run-android-upload-to-bintray: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:bintrayUpload - -# Uploads the compiled Android SDK SNAPSHOT to oss.jfrog.org -.PHONY: run-android-upload-to-artifactory -run-android-upload-to-artifactory: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=all :MapboxGLAndroidSDK:artifactoryPublish - -# Dump system graphics information for the test app -.PHONY: android-gfxinfo -android-gfxinfo: - adb shell dumpsys gfxinfo com.mapbox.mapboxsdk.testapp reset - -# Generates Activity sanity tests -.PHONY: test-code-android -test-code-android: - node platform/android/scripts/generate-test-code.js - -# Runs checkstyle and lint on the java code -.PHONY: android-check -android-check : android-ktlint android-checkstyle android-lint-sdk android-lint-test-app run-android-nitpick - -# Runs checkstyle on the java code -.PHONY: android-checkstyle -android-checkstyle: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:checkstyle :MapboxGLAndroidSDKTestApp:checkstyle - -# Runs checkstyle on the kotlin code -.PHONY: android-ktlint -android-ktlint: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none ktlint - -# Runs lint on the Android SDK java code -.PHONY: android-lint-sdk -android-lint-sdk: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:lint - -# Runs lint on the Android test app java code -.PHONY: android-lint-test-app -android-lint-test-app: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDKTestApp:lint - -# Generates javadoc from the Android SDK -.PHONY: android-javadoc -android-javadoc: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:javadocrelease - -# Generates platform/android/LICENSE.md file based on all Android project dependencies -.PHONY: android-license -android-license: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:licenseReleaseReport - python platform/android/scripts/generate-license.py - -# Symbolicate ndk stack traces for the arm-v7 abi -.PHONY: android-ndk-stack -android-ndk-stack: android-ndk-stack-arm-v7 - -# Open Android Studio if machine is macos -ifeq ($(HOST_PLATFORM), macos) -.PHONY: aproj -aproj: platform/android/gradle/configuration.gradle - open -b com.google.android.studio platform/android -endif - -# Creates the configuration needed to build with Android Studio -.PHONY: android-configuration -android-configuration: platform/android/gradle/configuration.gradle - cat platform/android/gradle/configuration.gradle - -# Updates Android's vendor submodules -.PHONY: android-update-vendor -android-update-vendor: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none updateVendorSubmodules - -# Run android nitpick script -.PHONY: run-android-nitpick -run-android-nitpick: android-update-vendor - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none androidNitpick - -# Creates a dependency graph using Graphviz -.PHONY: android-graph -android-graph: platform/android/gradle/configuration.gradle - cd platform/android && $(MBGL_ANDROID_GRADLE) -Pmapbox.abis=none :MapboxGLAndroidSDK:generateDependencyGraphMapboxLibraries - #### Miscellaneous targets ##################################################### .PHONY: style-code diff --git a/circle.yml b/circle.yml index 27cdf7c0e..129033809 100644 --- a/circle.yml +++ b/circle.yml @@ -137,20 +137,6 @@ workflows: # unique aspect of the build environment. # - {build type} is typically "debug" or "release". # - - android-debug-arm-v7-buck - - android-arm-template: - name: android-debug-arm-v8 - - android-arm-template: - name: android-gnustl-arm-v7 - stl: gnustl_shared - firebase_device_id: "flo" - firebase_device_os: "21" - image: android-ndk-r17c:1d5db0eb34 - abi: arm-v7 - - android-release: - filters: - tags: - only: /android-v.*/ - node-linux-release: filters: tags: @@ -405,21 +391,6 @@ commands: - 'ccache/v1/{{ arch }}/{{ .Environment.CIRCLE_JOB }}/{{ checksum ".circle-target-branch" }}/{{ checksum ".circle-merge-base" }}' - 'ccache/v1/{{ arch }}/{{ .Environment.CIRCLE_JOB }}/{{ checksum ".circle-target-branch" }}' - 'ccache/v1/{{ arch }}/{{ .Environment.CIRCLE_JOB }}' - save-gradle-cache: - steps: - - save_cache: - name: Save gradle cache - key: 'gradle/v1/{{ checksum "platform/android/gradle/dependencies.gradle" }}/{{ checksum "platform/android/build.gradle" }}/{{ checksum "platform/android/gradle/wrapper/gradle-wrapper.properties" }}' - paths: [ "/root/.gradle" ] - restore-gradle-cache: - steps: - - restore_cache: - name: Restore gradle cache - keys: - - 'gradle/v1/{{ checksum "platform/android/gradle/dependencies.gradle" }}/{{ checksum "platform/android/build.gradle" }}/{{ checksum "platform/android/gradle/wrapper/gradle-wrapper.properties" }}' - - 'gradle/v1' - - install-dependencies: parameters: node_modules: @@ -431,9 +402,6 @@ commands: mason: type: boolean default: true - gradle: - type: boolean - default: false steps: - checkout - when: @@ -446,9 +414,6 @@ commands: - when: condition: << parameters.mason >> steps: [ restore-mason_packages-cache ] - - when: - condition: << parameters.gradle >> - steps: [ restore-gradle-cache ] save-dependencies: parameters: node_modules: @@ -460,9 +425,6 @@ commands: mason: type: boolean default: true - gradle: - type: boolean - default: false steps: - when: condition: << parameters.node_modules >> @@ -473,10 +435,6 @@ commands: - when: condition: << parameters.mason >> steps: [ save-mason_packages-cache ] - - when: - condition: << parameters.gradle >> - steps: [ save-gradle-cache ] - setup-llvm-symbolizer: steps: @@ -722,7 +680,6 @@ jobs: - run: name: Code Generators command: | - platform/android/scripts/generate-style-code.js platform/default/include/mbgl/storage/offline_schema.js scripts/generate-file-lists.js scripts/generate-shaders.js @@ -822,216 +779,6 @@ jobs: runtime_wrapper: 'xvfb-run -s -noreset' metrics_params: '-p metrics/binary-size.json' - next-save - -# ------------------------------------------------------------------------------ - android-arm-template: - parameters: - stl: - type: string - default: "c++_static" - image: - type: string - default: android-ndk-r20:7b7c4b42cf - firebase_device_id: - type: string - default: sailfish - firebase_device_os: - type: string - default: "26" - abi: - type: string - default: "arm-v8" - docker: - - image: mbgl/<< parameters.image >> - resource_class: xlarge - working_directory: /src - environment: - LIBSYSCONFCPUS: 8 - JOBS: 8 - BUILDTYPE: Debug - IS_LOCAL_DEVELOPMENT: false - MBGL_ANDROID_STL: << parameters.stl >> - steps: - - install-dependencies: { gradle: true } - - check-if-this-job-can-be-skipped - - run: - name: Initialize vendor submodules - command: git submodule update --init platform/android/vendor - - run: - name: Check code style - command: make android-check - - run: - name: Run Android unit tests - command: make run-android-unit-test - - run: - name: Build libmapbox-gl.so for << parameters.abi >> - command: make android-lib-<< parameters.abi >> - - run: - name: Generate Espresso sanity tests - command: make test-code-android - - run: - name: Build Test APK - command: | - if [ -n "${MAPBOX_DEVELOPER_CONFIG_XML}" ]; then - echo "${MAPBOX_DEVELOPER_CONFIG_XML}" > platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/developer-config.xml - make android-ui-test-<< parameters.abi >> - fi - - save-dependencies: { gradle: true } - - conditionally-skip-firebase - - login-google-cloud-platform - - run: - name: Run instrumentation tests on Firebase - no_output_timeout: 20m - command: | - if [[ -n "${GCLOUD_SERVICE_ACCOUNT_JSON}" && -z "${SKIP_FIREBASE:-}" ]]; then - gcloud firebase test android models list - gcloud firebase test android run --type instrumentation \ - --app platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug/MapboxGLAndroidSDKTestApp-debug.apk \ - --test platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/androidTest/debug/MapboxGLAndroidSDKTestApp-debug-androidTest.apk \ - --device-ids << parameters.firebase_device_id >> --os-version-ids << parameters.firebase_device_os >> --locales en --orientations portrait --timeout 20m \ - --environment-variables coverage=true,coverageFile="/sdcard/coverage.ec" --directories-to-pull /sdcard --results-dir mapbox-android-sdk-${CIRCLE_BUILD_NUM} - coverageFile=`gsutil ls gs://test-lab-186672a0qp5bq-ycr70axads3nc/mapbox-android-sdk-${CIRCLE_BUILD_NUM}/**/*.ec | tail -1` - gsutil cp $coverageFile $PWD/platform/android/MapboxGLAndroidSDK/build/jacoco | true - fi - - run: - name: Parse and send Jacoco reports - command: | - if [[ $CIRCLE_BRANCH == master ]]; then - make android-create-jacoco-report && make android-parse-and-send-jacoco-report - fi - - store_artifacts: - path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/debug - destination: . - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/reports/lint-results.html - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/reports/lint-results.xml - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/lint-baseline.xml - - store_artifacts: - path: platform/android/MapboxGLAndroidSDKTestApp/build/reports/lint-results.html - - store_artifacts: - path: platform/android/MapboxGLAndroidSDKTestApp/build/reports/lint-results.xml - - store_artifacts: - path: platform/android/MapboxGLAndroidSDKTestApp/lint-baseline.xml - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/debug/obj - -# ------------------------------------------------------------------------------ - android-release: - docker: - - image: mbgl/android-ndk-r20:7b7c4b42cf - resource_class: xlarge - working_directory: /src - environment: - LIBSYSCONFCPUS: 8 - JOBS: 8 - BUILDTYPE: Release - IS_LOCAL_DEVELOPMENT: false - steps: - - install-dependencies: { gradle: true } - - check-if-this-job-can-be-skipped - - run: - name: Initialize vendor submodules - command: git submodule update --init platform/android/vendor - - run: - name: Android nitpick - command: make run-android-nitpick - - run: - name: Trigger core benchmark run - command: | - if [ -n "${MOBILE_METRICS_TOKEN}" ]; then - if [[ $CIRCLE_BRANCH == master ]]; then - curl -u ${MOBILE_METRICS_TOKEN}: -d build_parameters[CIRCLE_JOB]=android-core-benchmark https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/master - fi - fi - - run: - name: Trigger android benchmark run - command: | - if [ -n "${MOBILE_METRICS_TOKEN}" ]; then - if [[ $CIRCLE_BRANCH == master ]]; then - curl -u ${MOBILE_METRICS_TOKEN}: -d build_parameters[CIRCLE_JOB]=android-benchmark https://circleci.com/api/v1.1/project/github/mapbox/mobile-metrics/tree/master - fi - fi - - run: - name: Generate Maven credentials - command: | - if [ -n "${BINTRAY_USER}" ]; then - echo "BINTRAY_USER=$BINTRAY_USER - BINTRAY_API_KEY=$BINTRAY_API_KEY - GPG_PASSPHRASE=$GPG_PASSPHRASE" - fi - - run: - name: Update version name - command: | - if [[ $CIRCLE_TAG == android-v* ]]; then - sed -i -e "s/^VERSION_NAME=.*/VERSION_NAME=${CIRCLE_TAG:9}/" platform/android/MapboxGLAndroidSDK/gradle.properties - fi - - run: - name: Build package - command: make apackage - - run: - name: Build release Test App - command: make android - - run: - name: Generate javadoc - command: make android-javadoc - - save-dependencies: { gradle: true } - - run: - name: gzip debugable .so files - command: | - gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/arm64-v8a/libmapbox-gl.so && \ - gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/armeabi-v7a/libmapbox-gl.so && \ - gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86/libmapbox-gl.so && \ - gzip platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86_64/libmapbox-gl.so - - store_artifacts: - path: platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/release - destination: . - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/arm64-v8a/libmapbox-gl.so.gz - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/armeabi-v7a/libmapbox-gl.so.gz - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86/libmapbox-gl.so.gz - - store_artifacts: - path: platform/android/MapboxGLAndroidSDK/build/intermediates/cmake/release/obj/x86_64/libmapbox-gl.so.gz - - run: - name: Trigger external deploy steps - command: | - export VERSION_TAG=${CIRCLE_TAG} - export GITHUB_TOKEN=${DANGER_GITHUB_API_TOKEN} - export DOCS_REPO="android-docs" - scripts/trigger-maps-documentation-deploy-steps.sh - background: true -# - run: -# name: Record size -# command: platform/android/scripts/metrics.sh -# ------------------------------------------------------------------------------ - android-debug-arm-v7-buck: - docker: - - image: mbgl/android-ndk-r17c-buck:07c5ef2e71 - working_directory: /src - environment: - LIBSYSCONFCPUS: 2 - JOBS: 2 - BUILDTYPE: Debug - ANDROID_NDK: /android/sdk/ndk-bundle - steps: - - checkout - - npm-install - - prepare-environment - - check-if-this-job-can-be-skipped - - run: - name: Checkout submodules - command: | - git submodule update --init - git submodule foreach git submodule update --init - - run: - name: Build Android library - command: | - cd misc/buck - buck build mapbox-gl-native:android-core - # ------------------------------------------------------------------------------ node-linux-release: docker: diff --git a/platform/android/.editorconfig b/platform/android/.editorconfig deleted file mode 100644 index 8a6c90a0d..000000000 --- a/platform/android/.editorconfig +++ /dev/null @@ -1,4 +0,0 @@ -[*.{kt,kts}] -indent_size=2 -insert_final_newline=false -disabled_rules=no-wildcard-imports \ No newline at end of file diff --git a/platform/android/.project b/platform/android/.project deleted file mode 100644 index 3964dd3f5..000000000 --- a/platform/android/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - android - Project android created by Buildship. - - - - - org.eclipse.buildship.core.gradleprojectbuilder - - - - - - org.eclipse.buildship.core.gradleprojectnature - - diff --git a/platform/android/.settings/org.eclipse.buildship.core.prefs b/platform/android/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index e8895216f..000000000 --- a/platform/android/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,2 +0,0 @@ -connection.project.dir= -eclipse.preferences.version=1 diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index c1649eef0..b9a7a05ac 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -1,7 +1,10 @@ # Changelog for the Mapbox Maps SDK for Android - Mapbox welcomes participation and contributions from everyone. If you'd like to do so please see the [`Contributing Guide`](https://github.com/mapbox/mapbox-gl-native/blob/master/CONTRIBUTING.md) first to get started. +## Notice +**Work on the Mapbox Maps SDK for Android is now happening at https://github.com/mapbox/mapbox-gl-native-android.** +Please see the README at https://github.com/mapbox/mapbox-gl-native-android for the most up-to-date information on the Maps SDK for Android. + ## master - Added support for [image expression](https://docs.mapbox.com/mapbox-gl-js/style-spec/#expressions-types-image) in core library. Runtime APIs for image expression will be implemented separately. [#15877](https://github.com/mapbox/mapbox-gl-native/pull/15877) - Make network requests for expired resources lower priority than requests for new resources. ([#15950](https://github.com/mapbox/mapbox-gl-native/pull/15950)) diff --git a/platform/android/DISTRIBUTE.md b/platform/android/DISTRIBUTE.md deleted file mode 100644 index 8f2bec19d..000000000 --- a/platform/android/DISTRIBUTE.md +++ /dev/null @@ -1,57 +0,0 @@ -# Distributing Mapbox GL Native for Android - -Depending on your use case, you may want to support all or just a subset of [Android ABIs](http://developer.android.com/ndk/guides/abis.html). This document covers building an `.aar` file from the Mapbox Maps SDK for Android and building `.so` files for specific ABIs. In normal circumstances an application developer will use [APK splits](https://developer.android.com/studio/build/configure-apk-splits.html) to optimize this at application level. - -##### Build types - -With a `BUILDTYPE` var you can specify the build type for the `.so` and `.aar` files: - -```bash -BUILDTYPE=Debug or BUILDTYPE=Release -``` - -##### Creating an Android Archive file that supports all ABIs - -```sh -BUILDTYPE=Release make apackage -``` - -This will build native libraries to support following ABIs: -- armeabi -- armeabi-v7a -- arm64-v8a -- x86 -- x86_64 -- mips - -After succesfully finish building the native libraries, gradle will build the MapboxAndroidSDK module and generate an Android Archive file in `MapboxAndroidSDK/build/outputs/aar `. The packaged native libraries can be found in `MapboxAndroidSDK/src/main/jniLibs/`. - -##### Build native libraries for a specific ABI - -```sh -make android-lib-%% -``` - -In the command above you'll need to replace `%%` with an ABI key listed below: - -| ABI Key | Android ABI | -| ------- | ----------- | -| arm-v5 | armeabi | -| arm-v7 | armeabi-v7a | -| arm-v8 | arm64-v8a | -| x86 | x86 | -| x86-64 | x86_64 | -| mips | mips | - -For example, to build the arm64-v8a ABI the Makefile target would be: - -```sh -make android-lib-arm-v8 -``` - -It's also possible to build multiple ABI (without having to build them all) by running the Makefile multiple times. For example to build arm64-v8a and x86_64 in the same project the commands would be: - -```sh -make android-lib-arm-v8 -make android-lib-arm-x86-64 -``` diff --git a/platform/android/LICENSE.md b/platform/android/LICENSE.md deleted file mode 100644 index 8423a34a6..000000000 --- a/platform/android/LICENSE.md +++ /dev/null @@ -1,227 +0,0 @@ - -## Additional Mapbox GL licenses -Mapbox GL uses portions of the Android Arch-Common. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Arch-Runtime. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Lifecycle LiveData. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Lifecycle LiveData Core. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Lifecycle Runtime. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Lifecycle ViewModel. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Lifecycle-Common. -URL: [https://developer.android.com/topic/libraries/architecture/index.html](https://developer.android.com/topic/libraries/architecture/index.html) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Annotations. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Async Layout Inflater. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library collections. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library compat. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Coordinator Layout. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library core UI. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library core utils. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Cursor Adapter. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Custom View. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Custom View. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Document File. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Drawer Layout. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library fragment. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Interpolators. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library loader. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Local Broadcast Manager. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Print. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library Sliding Pane Layout. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Android Support Library View Pager. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Gson. -License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Mapbox Accounts SDK for Android. -URL: [https://github.com/mapbox/mapbox-accounts-android](https://github.com/mapbox/mapbox-accounts-android) -License: [Mapbox Terms of Service](https://www.mapbox.com/tos/) - -=========================================================================== - -Mapbox GL uses portions of the Mapbox Android Core Library. -URL: [https://github.com/mapbox/mapbox-events-android](https://github.com/mapbox/mapbox-events-android) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Mapbox Android Gestures Library. -URL: [https://github.com/mapbox/mapbox-gestures-android](https://github.com/mapbox/mapbox-gestures-android) -License: [BSD](https://opensource.org/licenses/BSD-2-Clause) - -=========================================================================== - -Mapbox GL uses portions of the Mapbox Android Telemetry Library. -URL: [https://github.com/mapbox/mapbox-events-android](https://github.com/mapbox/mapbox-events-android) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Mapbox Java SDK. -URL: [https://github.com/mapbox/mapbox-java](https://github.com/mapbox/mapbox-java) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Mapbox Java SDK. -URL: [https://github.com/mapbox/mapbox-java](https://github.com/mapbox/mapbox-java) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the OkHttp. -License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Okio. -License: [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the SoLoader. -URL: [https://github.com/facebook/soloader](https://github.com/facebook/soloader) -License: [Apache-2](https://github.com/facebook/soloader/blob/master/LICENSE) - -=========================================================================== - -Mapbox GL uses portions of the VersionedParcelable and friends. -URL: [http://developer.android.com/tools/extras/support-library.html](http://developer.android.com/tools/extras/support-library.html) -License: [The Apache Software License](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - -Mapbox GL uses portions of the Gradle License Plugin. -URL: [https://github.com/jaredsburrows/gradle-license-plugin](https://github.com/jaredsburrows/gradle-license-plugin) -License: [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - -=========================================================================== - diff --git a/platform/android/MapboxGLAndroidSDK/.gitignore b/platform/android/MapboxGLAndroidSDK/.gitignore deleted file mode 100644 index e44384742..000000000 --- a/platform/android/MapboxGLAndroidSDK/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -dependency-graph-mapbox-libraries.png -src/test/resources/mobile-event* diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle deleted file mode 100644 index 5ee3af61e..000000000 --- a/platform/android/MapboxGLAndroidSDK/build.gradle +++ /dev/null @@ -1,105 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: "com.jaredsburrows.license" -apply plugin: 'kotlin-android' -apply from: "${rootDir}/gradle/native-build.gradle" - -dependencies { - lintChecks project(":MapboxGLAndroidSDKLint") - api dependenciesList.mapboxAndroidTelemetry - api dependenciesList.mapboxJavaGeoJSON - api dependenciesList.mapboxAndroidGestures - api dependenciesList.mapboxAndroidAccounts - implementation dependenciesList.mapboxJavaTurf - implementation dependenciesList.supportAnnotations - implementation dependenciesList.supportFragmentV4 - implementation dependenciesList.okhttp3 - implementation dependenciesList.soLoader - testImplementation dependenciesList.junit - testImplementation dependenciesList.mockito - testImplementation dependenciesList.mockk - testImplementation dependenciesList.robolectric - testImplementation dependenciesList.kotlinLib - testImplementation dependenciesList.commonsIO -} - -android { - compileSdkVersion androidVersions.compileSdkVersion - - defaultConfig { - minSdkVersion androidVersions.minSdkVersion - targetSdkVersion androidVersions.targetSdkVersion - buildConfigField "String", "GIT_REVISION_SHORT", String.format("\"%s\"", getGitRevision()) - buildConfigField "String", "MAPBOX_SDK_IDENTIFIER", String.format("\"%s\"", "mapbox-maps-android") - buildConfigField "String", "MAPBOX_SDK_VERSION", String.format("\"%s\"", project.VERSION_NAME) - buildConfigField "String", "MAPBOX_VERSION_STRING", String.format("\"Mapbox/%s\"", project.VERSION_NAME) - buildConfigField "String", "MAPBOX_EVENTS_USER_AGENT", String.format("\"mapbox-maps-android/%s\"", project.VERSION_NAME) - consumerProguardFiles 'proguard-rules.pro' - } - - // build native libraries - List nativeTargets = ["mapbox-gl"] - if (project.hasProperty("mapbox.with_test")) { - nativeTargets.add("mbgl-test") - } - if (project.hasProperty("mapbox.with_benchmark")) { - nativeTargets.add("mbgl-benchmark") - } - nativeBuild(nativeTargets) - - // avoid naming conflicts, force usage of prefix - resourcePrefix 'mapbox_' - - sourceSets { - // limit amount of exposed library resources - main.res.srcDirs += 'src/main/res-public' - } - - lintOptions { - disable 'MissingTranslation', 'TypographyQuotes', 'ObsoleteLintCustomCheck', 'MissingPermission', 'WrongThreadInterprocedural' - checkAllWarnings true - warningsAsErrors false - } - - testOptions { - unitTests { - returnDefaultValues true - - // Roboelectric 4.0 required config - // http://robolectric.org/migrating/#migrating-to-40 - includeAndroidResources = true - } - } - - buildTypes { - debug { - testCoverageEnabled true - jniDebuggable true - } - } -} - -licenseReport { - generateHtmlReport = false - generateJsonReport = true - copyHtmlReportToAssets = false - copyJsonReportToAssets = false -} - -def static getGitRevision() { - def cmd = "git rev-parse --short HEAD" - def proc = cmd.execute() - def ref = proc.text.trim() - return ref -} - -configurations { - all*.exclude group: 'commons-logging', module: 'commons-logging' - all*.exclude group: 'commons-collections', module: 'commons-collections' -} -apply from: "${rootDir}/gradle/gradle-javadoc.gradle" -apply from: "${rootDir}/gradle/gradle-checkstyle.gradle" -apply from: "${rootDir}/gradle/gradle-dependencies-graph.gradle" -apply from: "${rootDir}/gradle/gradle-update-vendor-modules.gradle" -apply from: "${rootDir}/gradle/android-nitpick.gradle" -apply from: "${rootDir}/gradle/gradle-bintray.gradle" -apply from: "${rootDir}/gradle/jacoco-report.gradle" \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/gradle.properties b/platform/android/MapboxGLAndroidSDK/gradle.properties deleted file mode 100644 index e3fedcde0..000000000 --- a/platform/android/MapboxGLAndroidSDK/gradle.properties +++ /dev/null @@ -1,9 +0,0 @@ -VERSION_NAME=8.5.0-SNAPSHOT - -# Only build native dependencies for the current ABI -# See https://code.google.com/p/android/issues/detail?id=221098#c20 -android.buildOnlyTargetAbi=true - -# Roboelectric 4.0 required config for < Android Studio v3.3.0 -# http://robolectric.org/migrating/#migrating-to-40 -android.enableUnitTestBinaryResources=true \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/lint.xml b/platform/android/MapboxGLAndroidSDK/lint.xml deleted file mode 100644 index 3a244199a..000000000 --- a/platform/android/MapboxGLAndroidSDK/lint.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro b/platform/android/MapboxGLAndroidSDK/proguard-rules.pro deleted file mode 100644 index f592efa6b..000000000 --- a/platform/android/MapboxGLAndroidSDK/proguard-rules.pro +++ /dev/null @@ -1,41 +0,0 @@ -# By default, the flags in this file are appended to flags specified -# in ../sdk/tools/proguard/proguard-android.txt, -# contents of this file will be appended into proguard-android.txt --keepattributes Signature, *Annotation*, EnclosingMethod - -# Reflection on classes from native code --keep class com.google.gson.JsonArray { *; } --keep class com.google.gson.JsonElement { *; } --keep class com.google.gson.JsonObject { *; } --keep class com.google.gson.JsonPrimitive { *; } --dontnote com.google.gson.** - -# dontnote for keeps the entry point x but not the descriptor class y --dontnote com.mapbox.mapboxsdk.maps.MapboxMap$OnFpsChangedListener --dontnote com.mapbox.mapboxsdk.style.layers.PropertyValue --dontnote com.mapbox.mapboxsdk.maps.MapboxMap --dontnote com.mapbox.mapboxsdk.maps.MapboxMapOptions --dontnote com.mapbox.mapboxsdk.log.LoggerDefinition --dontnote com.mapbox.android.core.location.LocationEnginePriority - -# config for okhttp 3.11.0, https://github.com/square/okhttp/pull/3354 --dontwarn javax.annotation.** --dontnote okhttp3.internal.** --dontwarn org.codehaus.** - -# config for mapbox-sdk-geojson:3.0.1 --keep class com.mapbox.geojson.** { *; } --dontwarn com.google.auto.value.** - -# config for additional notes --dontnote org.robolectric.Robolectric --dontnote libcore.io.Memory --dontnote com.google.protobuf.** --dontnote android.net.** --dontnote org.apache.http.** - -# config for mapbox-sdk-services -# while we don't include this dependency directly -# a large amount of users combine it with our SDK -# we aren't able to provide a proguard config in that project (jar vs aar) --dontwarn com.sun.xml.internal.ws.spi.db.* \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml deleted file mode 100644 index dcbec69bf..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/AndroidManifest.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/AccountsManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/AccountsManager.java deleted file mode 100644 index 5b8f10e01..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/AccountsManager.java +++ /dev/null @@ -1,170 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.content.Context; -import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.support.annotation.NonNull; -import android.support.annotation.VisibleForTesting; -import android.text.TextUtils; -import android.text.format.DateUtils; - -import com.mapbox.android.accounts.v1.MapboxAccounts; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.log.Logger; - -import static com.mapbox.mapboxsdk.constants.MapboxConstants.KEY_PREFERENCE_SKU_TOKEN; - -/** - * REMOVAL OR MODIFICATION OF THE FOLLOWING CODE VIOLATES THE MAPBOX TERMS - * OF SERVICE - *

- * The following code is used to access Mapbox's Mapping APIs. - *

- * Removal or modification of this code when used with Mapbox's Mapping APIs - * can result in termination of your agreement and/or your account with - * Mapbox. - *

- * Using this code to access Mapbox Mapping APIs from outside the Mapbox Maps - * SDK also violates the Mapbox Terms of Service. On Android, Mapping APIs - * should be accessed using the methods documented at - * https://www.mapbox.com/android. - *

- * You can access the Mapbox Terms of Service at https://www.mapbox.com/tos/ - */ -class AccountsManager { - private static final String TAG = "Mbgl-AccountsManager"; - private static final String PREFERENCE_USER_ID = "com.mapbox.mapboxsdk.accounts.userid"; - private static final String PREFERENCE_TIMESTAMP = "com.mapbox.mapboxsdk.accounts.timestamp"; - - private SharedPreferences sharedPreferences; - private String skuToken; - private long timestamp; - private boolean isManaged; - - AccountsManager() { - isManaged = isSkuTokenManaged(); - initialize(); - } - - @VisibleForTesting - AccountsManager(SharedPreferences sharedPreferences, boolean isManaged) { - this.sharedPreferences = sharedPreferences; - this.isManaged = isManaged; - initialize(); - } - - private void initialize() { - retrieveSkuTokenAndTimestamp(); - if (isManaged) { - String userId = validateUserId(); - validateRotation(userId); - } - } - - private boolean isSkuTokenManaged() { - boolean value = MapboxConstants.DEFAULT_MANAGE_SKU_TOKEN; - try { - // Try getting a custom value from the app Manifest - ApplicationInfo appInfo = retrieveApplicationInfo(); - if (appInfo.metaData != null) { - value = appInfo.metaData.getBoolean( - MapboxConstants.KEY_META_DATA_MANAGE_SKU_TOKEN, - MapboxConstants.DEFAULT_MANAGE_SKU_TOKEN - ); - } - } catch (Exception exception) { - Logger.e(TAG, "Failed to read the package metadata: ", exception); - } - - return value; - } - - private ApplicationInfo retrieveApplicationInfo() throws PackageManager.NameNotFoundException { - return Mapbox.getApplicationContext().getPackageManager().getApplicationInfo( - Mapbox.getApplicationContext().getPackageName(), - PackageManager.GET_META_DATA); - } - - private void retrieveSkuTokenAndTimestamp() { - SharedPreferences sharedPreferences = getSharedPreferences(); - skuToken = sharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, ""); - timestamp = sharedPreferences.getLong(PREFERENCE_TIMESTAMP, 0L); - } - - private String validateUserId() { - SharedPreferences sharedPreferences = getSharedPreferences(); - String userId = sharedPreferences.getString(PREFERENCE_USER_ID, ""); - if (TextUtils.isEmpty(userId)) { - userId = generateUserId(); - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.putString(PREFERENCE_USER_ID, userId); - editor.apply(); - } - - return userId; - } - - private void validateRotation(String userId) { - if (TextUtils.isEmpty(skuToken) || timestamp == 0L) { - skuToken = generateSkuToken(userId); - timestamp = persistRotation(skuToken); - } - } - - String getSkuToken() { - if (isManaged) { - if (isExpired()) { - SharedPreferences sharedPreferences = getSharedPreferences(); - String userId = sharedPreferences.getString(PREFERENCE_USER_ID, ""); - skuToken = generateSkuToken(userId); - timestamp = persistRotation(skuToken); - } - } else { - SharedPreferences sharedPreferences = getSharedPreferences(); - String notManagedSkuToken = sharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, ""); - skuToken = notManagedSkuToken; - } - return skuToken; - } - - private boolean isExpired() { - return isExpired(getNow(), timestamp); - } - - static boolean isExpired(long now, long then) { - return ((now - then) > DateUtils.HOUR_IN_MILLIS); - } - - private long persistRotation(String skuToken) { - long now = getNow(); - SharedPreferences.Editor editor = getSharedPreferences().edit(); - editor.putLong(PREFERENCE_TIMESTAMP, now); - editor.putString(KEY_PREFERENCE_SKU_TOKEN, skuToken); - editor.apply(); - return now; - } - - @NonNull - private SharedPreferences getSharedPreferences() { - if (sharedPreferences == null) { - sharedPreferences = Mapbox.getApplicationContext() - .getSharedPreferences(MapboxConstants.MAPBOX_SHARED_PREFERENCES, Context.MODE_PRIVATE); - } - return sharedPreferences; - } - - static long getNow() { - return System.currentTimeMillis(); - } - - @NonNull - private String generateUserId() { - return MapboxAccounts.obtainEndUserId(); - } - - @NonNull - private String generateSkuToken(String userId) { - return MapboxAccounts.obtainMapsSkuUserToken(userId); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java deleted file mode 100644 index ee9f4b3b6..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoader.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.mapbox.mapboxsdk; - -import com.mapbox.mapboxsdk.log.Logger; - -/** - * Loads the mapbox-gl shared library - *

- * By default uses the {@link System#loadLibrary(String)}, - * use {@link #setLibraryLoader(LibraryLoader)} to provide an alternative library loading hook. - *

- */ -public abstract class LibraryLoader { - - private static final String TAG = "Mbgl-LibraryLoader"; - - private static final LibraryLoader DEFAULT = Mapbox.getModuleProvider() - .createLibraryLoaderProvider() - .getDefaultLibraryLoader(); - - private static volatile LibraryLoader loader = DEFAULT; - - private static boolean loaded; - - /** - * Set the library loader that loads the shared library. - * - * @param libraryLoader the library loader - */ - public static void setLibraryLoader(LibraryLoader libraryLoader) { - loader = libraryLoader; - } - - /** - * Loads "libmapbox-gl.so" native shared library. - *

- * Catches UnsatisfiedLinkErrors and prints a warning to logcat. - *

- */ - public static void load() { - try { - if (!loaded) { - loaded = true; - loader.load("mapbox-gl"); - } - } catch (UnsatisfiedLinkError error) { - loaded = false; - String message = "Failed to load native shared library."; - Logger.e(TAG, message, error); - MapStrictMode.strictModeViolation(message, error); - } - } - - public abstract void load(String name); -} - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoaderProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoaderProvider.java deleted file mode 100644 index c9d98bf76..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/LibraryLoaderProvider.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.mapbox.mapboxsdk; - -/** - * Injects the default library loader. - */ -public interface LibraryLoaderProvider { - - /** - * Creates and returns a the default Library Loader. - * - * @return the default library loader - */ - LibraryLoader getDefaultLibraryLoader(); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictMode.java deleted file mode 100644 index c7f995964..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictMode.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.mapbox.mapboxsdk; - -/** - * Using this class you can enable a strict mode that will throw the {@link MapStrictModeException} - * whenever the map would fail silently otherwise. - */ -public class MapStrictMode { - private static volatile boolean strictModeEnabled; - - /** - * Set the strict mode that will throw the {@link MapStrictModeException} - * whenever the map would fail silently otherwise. - * - * @param strictModeEnabled true to enable the strict mode, false otherwise - */ - public static synchronized void setStrictModeEnabled(boolean strictModeEnabled) { - MapStrictMode.strictModeEnabled = strictModeEnabled; - } - - /** - * Internal use. Called whenever the strict mode violation occurs. - */ - public static void strictModeViolation(String message) { - if (strictModeEnabled) { - throw new MapStrictModeException(message); - } - } - - /** - * Internal use. Called whenever the strict mode violation occurs. - */ - public static void strictModeViolation(String message, Throwable throwable) { - if (strictModeEnabled) { - throw new MapStrictModeException(String.format("%s - %s", message, throwable)); - } - } - - /** - * Internal use. Called whenever the strict mode violation occurs. - */ - public static void strictModeViolation(Throwable throwable) { - if (strictModeEnabled) { - throw new MapStrictModeException(String.format("%s", throwable)); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictModeException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictModeException.java deleted file mode 100644 index 5bfddabf9..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/MapStrictModeException.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mapbox.mapboxsdk; - -class MapStrictModeException extends RuntimeException { - MapStrictModeException(String message) { - super(String.format("Map detected an error that would fail silently otherwise: %s", message)); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java deleted file mode 100644 index 35f7a5975..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/Mapbox.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.res.AssetManager; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.UiThread; - -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.maps.TelemetryDefinition; -import com.mapbox.mapboxsdk.net.ConnectivityReceiver; -import com.mapbox.mapboxsdk.storage.FileSource; -import com.mapbox.mapboxsdk.utils.ThreadUtils; - -/** - * The entry point to initialize the Mapbox Android SDK. - *

- * Obtain a reference by calling {@link #getInstance(Context, String)}. Usually this class is configured in - * Application#onCreate() and is responsible for the active access token, application context, and - * connectivity state. - *

- */ -@UiThread -@SuppressLint("StaticFieldLeak") -@Keep -public final class Mapbox { - - private static final String TAG = "Mbgl-Mapbox"; - private static ModuleProvider moduleProvider; - private static Mapbox INSTANCE; - - private Context context; - @Nullable - private String accessToken; - @Nullable - private TelemetryDefinition telemetry; - @Nullable - private AccountsManager accounts; - - /** - * Get an instance of Mapbox. - *

- * This class manages the Mapbox access token, application context, and connectivity state. - *

- * - * @param context Android context which holds or is an application context - * @param accessToken Mapbox access token - * @return the single instance of Mapbox - */ - @UiThread - @NonNull - public static synchronized Mapbox getInstance(@NonNull Context context, @Nullable String accessToken) { - ThreadUtils.init(context); - ThreadUtils.checkThread(TAG); - if (INSTANCE == null) { - Context appContext = context.getApplicationContext(); - FileSource.initializeFileDirsPaths(appContext); - INSTANCE = new Mapbox(appContext, accessToken); - if (isAccessTokenValid(accessToken)) { - initializeTelemetry(); - INSTANCE.accounts = new AccountsManager(); - } - ConnectivityReceiver.instance(appContext); - } - return INSTANCE; - } - - Mapbox(@NonNull Context context, @Nullable String accessToken) { - this.context = context; - this.accessToken = accessToken; - } - - /** - * Get the current active access token for this application. - * - * @return Mapbox access token - */ - @Nullable - public static String getAccessToken() { - validateMapbox(); - return INSTANCE.accessToken; - } - - /** - * Set the current active accessToken. - */ - public static void setAccessToken(String accessToken) { - validateMapbox(); - INSTANCE.accessToken = accessToken; - - // cleanup telemetry which is dependent on an access token - if (INSTANCE.telemetry != null) { - INSTANCE.telemetry.disableTelemetrySession(); - INSTANCE.telemetry = null; - } - - // initialize components dependent on a token - if (isAccessTokenValid(accessToken)) { - initializeTelemetry(); - INSTANCE.accounts = new AccountsManager(); - } else { - INSTANCE.accounts = null; - } - FileSource.getInstance(getApplicationContext()).setAccessToken(accessToken); - } - - /** - * Returns a SKU token, refreshed if necessary. This method is meant for internal SDK - * usage only. - * - * @return the SKU token - */ - public static String getSkuToken() { - if (INSTANCE.accounts == null) { - throw new MapboxConfigurationException( - "A valid access token parameter is required when using a Mapbox service." - + "\nPlease see https://www.mapbox.com/help/create-api-access-token/ to learn how to create one." - + "\nMore information in this guide https://www.mapbox.com/help/first-steps-android-sdk/#access-tokens." - + "Currently provided token is: " + INSTANCE.accessToken); - } - return INSTANCE.accounts.getSkuToken(); - } - - /** - * Application context - * - * @return the application context - */ - @NonNull - public static Context getApplicationContext() { - validateMapbox(); - return INSTANCE.context; - } - - /** - * Manually sets the connectivity state of the app. This is useful for apps which control their - * own connectivity state and want to bypass any checks to the ConnectivityManager. - * - * @param connected flag to determine the connectivity state, true for connected, false for - * disconnected, and null for ConnectivityManager to determine. - */ - public static synchronized void setConnected(Boolean connected) { - validateMapbox(); - ConnectivityReceiver.instance(INSTANCE.context).setConnected(connected); - } - - /** - * Determines whether we have an internet connection available. Please do not rely on this - * method in your apps. This method is used internally by the SDK. - * - * @return true if there is an internet connection, false otherwise - */ - public static synchronized Boolean isConnected() { - validateMapbox(); - return ConnectivityReceiver.instance(INSTANCE.context).isConnected(); - } - - /** - * Initializes telemetry - */ - private static void initializeTelemetry() { - try { - INSTANCE.telemetry = getModuleProvider().obtainTelemetry(); - } catch (Exception exception) { - String message = "Error occurred while initializing telemetry"; - Logger.e(TAG, message, exception); - MapStrictMode.strictModeViolation(message, exception); - } - } - - /** - * Get an instance of Telemetry if initialised - * - * @return instance of telemetry - */ - @Nullable - public static TelemetryDefinition getTelemetry() { - return INSTANCE.telemetry; - } - - /** - * Get the module provider - * - * @return moduleProvider - */ - @NonNull - public static ModuleProvider getModuleProvider() { - if (moduleProvider == null) { - moduleProvider = new ModuleProviderImpl(); - } - return moduleProvider; - } - - /** - * Runtime validation of Mapbox creation. - */ - private static void validateMapbox() { - if (INSTANCE == null) { - throw new MapboxConfigurationException(); - } - } - - /** - * Runtime validation of Mapbox access token - * - * @param accessToken the access token to validate - * @return true is valid, false otherwise - */ - static boolean isAccessTokenValid(@Nullable String accessToken) { - if (accessToken == null) { - return false; - } - - accessToken = accessToken.trim().toLowerCase(MapboxConstants.MAPBOX_LOCALE); - return accessToken.length() != 0 && (accessToken.startsWith("pk.") || accessToken.startsWith("sk.")); - } - - /** - * Internal use. Check if the {@link Mapbox#INSTANCE} is present. - */ - public static boolean hasInstance() { - return INSTANCE != null; - } - - /** - * Internal use. Returns AssetManager. - * - * @return the asset manager - */ - private static AssetManager getAssetManager() { - return getApplicationContext().getResources().getAssets(); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProvider.java deleted file mode 100644 index 9749290eb..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.http.HttpRequest; -import com.mapbox.mapboxsdk.maps.TelemetryDefinition; - -/** - * Injects concrete instances of configurable abstractions - */ -public interface ModuleProvider { - - /** - * Create a new concrete implementation of HttpRequest. - * - * @return a new instance of an HttpRequest - */ - @NonNull - HttpRequest createHttpRequest(); - - /** - * Get the concrete implementation of TelemetryDefinition - * - * @return a single instance of Telemetry - */ - @Nullable - TelemetryDefinition obtainTelemetry(); - - - /** - * Get the concrete implementation of LibraryLoaderProvider - * - * @return a new instance of LibraryLoaderProvider - */ - @NonNull - LibraryLoaderProvider createLibraryLoaderProvider(); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProviderImpl.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProviderImpl.java deleted file mode 100644 index 2de111c97..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/ModuleProviderImpl.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.http.HttpRequest; -import com.mapbox.mapboxsdk.maps.TelemetryDefinition; -import com.mapbox.mapboxsdk.module.http.HttpRequestImpl; -import com.mapbox.mapboxsdk.module.loader.LibraryLoaderProviderImpl; -import com.mapbox.mapboxsdk.module.telemetry.TelemetryImpl; - -public class ModuleProviderImpl implements ModuleProvider { - - @Override - @NonNull - public HttpRequest createHttpRequest() { - return new HttpRequestImpl(); - } - - @Override - @Nullable - public TelemetryDefinition obtainTelemetry() { - return new TelemetryImpl(); - } - - @NonNull - @Override - public LibraryLoaderProvider createLibraryLoaderProvider() { - return new LibraryLoaderProviderImpl(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java deleted file mode 100644 index 8ea226649..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Annotation.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.support.annotation.NonNull; - -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -/** - * Annotation is an overlay on top of a Map. - *

- * Known subclasses are {@link Polygon}, {@link Polyline} and {@link Marker}. - *

- *

- * This class manages attachment to a map and identification, but does not require - * content to be placed at a geographical point. - *

- * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public abstract class Annotation implements Comparable { - - /** - *

- * The annotation id - *

- * Internal C++ id is stored as unsigned int. - */ - private long id = -1; // -1 unless added to a MapView - protected MapboxMap mapboxMap; - protected MapView mapView; - - protected Annotation() { - } - - /** - *

- * Gets the annotation's unique ID. - *

- * This ID is unique for a MapView instance and is suitable for associating your own extra - * data with. - * - * @return the assigned id. - */ - public long getId() { - return id; - } - - /** - * Do not use this method, used internally by the SDK. - */ - public void remove() { - if (mapboxMap == null) { - return; - } - mapboxMap.removeAnnotation(this); - } - - /** - * Do not use this method, used internally by the SDK. - * - * @param id the assigned id - */ - public void setId(long id) { - this.id = id; - } - - /** - * Do not use this method, used internally by the SDK. - * - * @param mapboxMap the hosting mapbox map - */ - public void setMapboxMap(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - } - - /** - * Gets the hosting mapbox map. - * - * @return the MapboxMap - */ - protected MapboxMap getMapboxMap() { - return mapboxMap; - } - - /** - * Do not use this method, used internally by the SDK. - * - * @param mapView the hosting map view - */ - public void setMapView(MapView mapView) { - this.mapView = mapView; - } - - /** - * Gets the hosting map view. - * - * @return The MapView - */ - protected MapView getMapView() { - return mapView; - } - - /** - * Compares this Annotation object with another Annotation. - * - * @param annotation Another Annotation to compare with this object. - * @return returns 0 if id's match, 1 if id is lower, -1 if id is higher of another Annotation - */ - @Override - public int compareTo(@NonNull Annotation annotation) { - if (id < annotation.getId()) { - return 1; - } else if (id > annotation.getId()) { - return -1; - } - return 0; - } - - /** - * Checks if this Annotation object is equal to another Annotation. - * - * @param object Another Annotation to check equality with this object. - * @return returns true both id's match else returns false. - */ - @Override - public boolean equals(@Nullable Object object) { - if (this == object) { - return true; - } - if (object == null || !(object instanceof Annotation)) { - return false; - } - Annotation that = (Annotation) object; - return id == that.getId(); - } - - /** - * Gives an integer which can be used as the bucket number for storing elements of the set/map. - * This bucket number is the address of the element inside the set/map. There's no guarantee - * that this hash value will be consistent between different Java implementations, or even - * between different execution runs of the same program. - * - * @return integer value you can use for storing element. - */ - @Override - public int hashCode() { - return (int) (getId() ^ (getId() >>> 32)); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java deleted file mode 100644 index 220544815..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/ArrowDirection.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.support.annotation.IntDef; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Deprecated -class ArrowDirection { - @IntDef( {LEFT, RIGHT, TOP, BOTTOM}) - @Retention(RetentionPolicy.SOURCE) - @interface Value { - } - - static final int LEFT = 0; - static final int RIGHT = 1; - static final int TOP = 2; - static final int BOTTOM = 3; - - @Value - private final int value; - - ArrowDirection(@Value int value) { - this.value = value; - } - - @Value - public int getValue() { - return value; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java deleted file mode 100644 index 384ff8c0a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BaseMarkerOptions.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.os.Parcelable; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -/** - * Abstract builder class for composing custom Marker objects. - *

- * Extending this class requires implementing Parceable interface. - * - * @param Type of the marker to be composed - * @param Type of the builder to be used for composing a custom Marker - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public abstract class BaseMarkerOptions> implements Parcelable { - - protected LatLng position; - protected String snippet; - protected String title; - protected Icon icon; - - /** - * Set the geographical location of the Marker. - * - * @param position the location to position the {@link Marker}. - * @return the object for which the method was called. - */ - public T position(LatLng position) { - this.position = position; - return getThis(); - } - - /** - * Set the snippet of the Marker. - * - * @param snippet the snippet of the {@link Marker}. - * @return the object for which the method was called. - */ - public T snippet(String snippet) { - this.snippet = snippet; - return getThis(); - } - - /** - * Set the title of the Marker. - * - * @param title the title of the {@link Marker}. - * @return the object for which the method was called. - */ - public T title(String title) { - this.title = title; - return getThis(); - } - - /** - * Set the icon of the Marker. - * - * @param icon the icon of the {@link Marker}. - * @return the object for which the method was called. - */ - public T icon(Icon icon) { - this.icon = icon; - return getThis(); - } - - /** - * Set the icon of the Marker. - * - * @param icon the icon of the {@link Marker}. - * @return the object for which the method was called. - */ - public T setIcon(Icon icon) { - return icon(icon); - } - - /** - * Set the geographical location of the Marker. - * - * @param position the location to position the {@link Marker}. - * @return the object for which the method was called. - */ - public T setPosition(LatLng position) { - return position(position); - } - - /** - * Set the snippet of the Marker. - * - * @param snippet the snippet of the {@link Marker}. - * @return the object for which the method was called. - */ - public T setSnippet(String snippet) { - return snippet(snippet); - } - - /** - * Set the title of the Marker. - * - * @param title the title of the {@link Marker}. - * @return the object for which the method was called. - */ - public T setTitle(String title) { - return title(title); - } - - /** - * Get the instance of the object for which this method was called. - * - * @return the object for which the this method was called. - */ - public abstract T getThis(); - - /** - * Get the Marker. - * - * @return the Marker created from this builder. - */ - public abstract U getMarker(); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BasePointCollection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BasePointCollection.java deleted file mode 100644 index b94721a80..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BasePointCollection.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.support.annotation.Keep; - -import android.support.annotation.NonNull; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.util.ArrayList; -import java.util.List; - -/** - * Multipoint is an abstract annotation for combining geographical locations. - * - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public abstract class BasePointCollection extends Annotation { - - @Keep - private List points; - @Keep - private float alpha = 1.0f; - - protected BasePointCollection() { - super(); - points = new ArrayList<>(); - } - - /** - * Returns a copy of the points. - * - * @return A {@link List} of points. - */ - @NonNull - public List getPoints() { - return new ArrayList<>(points); - } - - /** - * Sets the points of this polyline. This method will take a copy of the points, so further - * mutations to points will have no effect on this polyline. - * - * @param points A {@link List} of {@link LatLng} points making up the polyline. - */ - public void setPoints(@NonNull List points) { - this.points = new ArrayList<>(points); - update(); - } - - /** - * Add a point to the polyline. - * - * @param point A {@link LatLng} point to be added. - */ - public void addPoint(LatLng point) { - points.add(point); - update(); - } - - /** - * Value between 0 and 1 defining the polyline alpha. - * - * @return float value between 0 and 1. - */ - public float getAlpha() { - return alpha; - } - - /** - * Set this {@link BasePointCollection}s alpha. - * - * @param alpha float value between 0 and 1. - */ - public void setAlpha(float alpha) { - this.alpha = alpha; - update(); - } - - abstract void update(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java deleted file mode 100644 index 0768f9c2e..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Bubble.java +++ /dev/null @@ -1,315 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; -import android.support.annotation.NonNull; - -@Deprecated -class Bubble extends Drawable { - - private RectF rect; - private float arrowWidth; - private float arrowHeight; - private float arrowPosition; - private float cornersRadius; - @NonNull - private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); - private float strokeWidth; - private Paint strokePaint; - private Path strokePath; - @NonNull - private Path path = new Path(); - - Bubble(@NonNull RectF rect, @NonNull ArrowDirection arrowDirection, float arrowWidth, float arrowHeight, - float arrowPosition, float cornersRadius, int bubbleColor, float strokeWidth, int strokeColor) { - this.rect = rect; - this.arrowWidth = arrowWidth; - this.arrowHeight = arrowHeight; - this.arrowPosition = arrowPosition; - this.cornersRadius = cornersRadius; - paint.setColor(bubbleColor); - this.strokeWidth = strokeWidth; - - if (strokeWidth > 0) { - strokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); - strokePaint.setColor(strokeColor); - strokePath = new Path(); - initPath(arrowDirection, path, strokeWidth); - initPath(arrowDirection, strokePath, 0); - } else { - initPath(arrowDirection, path, 0); - } - } - - @Override - protected void onBoundsChange(Rect bounds) { - super.onBoundsChange(bounds); - } - - @Override - public void draw(@NonNull Canvas canvas) { - if (strokeWidth > 0) { - canvas.drawPath(strokePath, strokePaint); - } - canvas.drawPath(path, paint); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { - paint.setAlpha(alpha); - } - - @Override - public void setColorFilter(ColorFilter cf) { - paint.setColorFilter(cf); - } - - @Override - public int getIntrinsicWidth() { - return (int) rect.width(); - } - - @Override - public int getIntrinsicHeight() { - return (int) rect.height(); - } - - private void initPath(@NonNull ArrowDirection arrowDirection, @NonNull Path path, float strokeWidth) { - switch (arrowDirection.getValue()) { - case ArrowDirection.LEFT: - if (cornersRadius <= 0) { - initLeftSquarePath(rect, path, strokeWidth); - break; - } - - if (strokeWidth > 0 && strokeWidth > cornersRadius) { - initLeftSquarePath(rect, path, strokeWidth); - break; - } - - initLeftRoundedPath(rect, path, strokeWidth); - break; - case ArrowDirection.TOP: - if (cornersRadius <= 0) { - initTopSquarePath(rect, path, strokeWidth); - break; - } - - if (strokeWidth > 0 && strokeWidth > cornersRadius) { - initTopSquarePath(rect, path, strokeWidth); - break; - } - - initTopRoundedPath(rect, path, strokeWidth); - break; - case ArrowDirection.RIGHT: - if (cornersRadius <= 0) { - initRightSquarePath(rect, path, strokeWidth); - break; - } - - if (strokeWidth > 0 && strokeWidth > cornersRadius) { - initRightSquarePath(rect, path, strokeWidth); - break; - } - - initRightRoundedPath(rect, path, strokeWidth); - break; - case ArrowDirection.BOTTOM: - if (cornersRadius <= 0) { - initBottomSquarePath(rect, path, strokeWidth); - break; - } - - if (strokeWidth > 0 && strokeWidth > cornersRadius) { - initBottomSquarePath(rect, path, strokeWidth); - break; - } - - initBottomRoundedPath(rect, path, strokeWidth); - break; - } - } - - private void initLeftSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(arrowWidth + rect.left + strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.width() - strokeWidth, rect.top + strokeWidth); - - path.lineTo(rect.right - strokeWidth, rect.bottom - strokeWidth); - - path.lineTo(rect.left + arrowWidth + strokeWidth, rect.bottom - strokeWidth); - - path.lineTo(rect.left + arrowWidth + strokeWidth, arrowHeight + arrowPosition - (strokeWidth / 2)); - path.lineTo(rect.left + strokeWidth + strokeWidth, arrowPosition + arrowHeight / 2); - path.lineTo(rect.left + arrowWidth + strokeWidth, arrowPosition + (strokeWidth / 2)); - - path.lineTo(rect.left + arrowWidth + strokeWidth, rect.top + strokeWidth); - - path.close(); - } - - private void initLeftRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(arrowWidth + rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.width() - cornersRadius - strokeWidth, rect.top + strokeWidth); - path.arcTo(new RectF(rect.right - cornersRadius, rect.top + strokeWidth, rect.right - strokeWidth, - cornersRadius + rect.top), 270, 90); - - path.lineTo(rect.right - strokeWidth, rect.bottom - cornersRadius - strokeWidth); - path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius, - rect.right - strokeWidth, rect.bottom - strokeWidth), 0, 90); - - path.lineTo(rect.left + arrowWidth + cornersRadius + strokeWidth, rect.bottom - strokeWidth); - - path.arcTo(new RectF(rect.left + arrowWidth + strokeWidth, rect.bottom - cornersRadius, - cornersRadius + rect.left + arrowWidth, rect.bottom - strokeWidth), 90, 90); - - path.lineTo(rect.left + arrowWidth + strokeWidth, arrowHeight + arrowPosition - (strokeWidth / 2)); - - path.lineTo(rect.left + strokeWidth + strokeWidth, arrowPosition + arrowHeight / 2); - - path.lineTo(rect.left + arrowWidth + strokeWidth, arrowPosition + (strokeWidth / 2)); - - path.lineTo(rect.left + arrowWidth + strokeWidth, rect.top + cornersRadius + strokeWidth); - - path.arcTo(new RectF(rect.left + arrowWidth + strokeWidth, rect.top + strokeWidth, cornersRadius - + rect.left + arrowWidth, cornersRadius + rect.top), 180, 90); - - path.close(); - } - - private void initTopSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(rect.left + arrowPosition + strokeWidth, rect.top + arrowHeight + strokeWidth); - - path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); - path.lineTo(rect.left + arrowWidth / 2 + arrowPosition, rect.top + strokeWidth + strokeWidth); - path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); - path.lineTo(rect.right - strokeWidth, rect.top + arrowHeight + strokeWidth); - - path.lineTo(rect.right - strokeWidth, rect.bottom - strokeWidth); - - path.lineTo(rect.left + strokeWidth, rect.bottom - strokeWidth); - - path.lineTo(rect.left + strokeWidth, rect.top + arrowHeight + strokeWidth); - - path.lineTo(rect.left + arrowPosition + strokeWidth, rect.top + arrowHeight + strokeWidth); - - path.close(); - } - - private void initTopRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(rect.left + Math.min(arrowPosition, cornersRadius) + strokeWidth, rect.top + arrowHeight - + strokeWidth); - path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); - path.lineTo(rect.left + arrowWidth / 2 + arrowPosition, rect.top + strokeWidth + strokeWidth); - path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.top + arrowHeight + strokeWidth); - path.lineTo(rect.right - cornersRadius - strokeWidth, rect.top + arrowHeight + strokeWidth); - - path.arcTo(new RectF(rect.right - cornersRadius, - rect.top + arrowHeight + strokeWidth, rect.right - strokeWidth, cornersRadius + rect.top + arrowHeight), - 270, 90); - path.lineTo(rect.right - strokeWidth, rect.bottom - cornersRadius - strokeWidth); - - path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius, - rect.right - strokeWidth, rect.bottom - strokeWidth), 0, 90); - path.lineTo(rect.left + cornersRadius + strokeWidth, rect.bottom - strokeWidth); - - path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius, - cornersRadius + rect.left, rect.bottom - strokeWidth), 90, 90); - - path.lineTo(rect.left + strokeWidth, rect.top + arrowHeight + cornersRadius + strokeWidth); - - path.arcTo(new RectF(rect.left + strokeWidth, rect.top + arrowHeight + strokeWidth, cornersRadius - + rect.left, cornersRadius + rect.top + arrowHeight), 180, 90); - - path.close(); - } - - private void initRightSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(rect.left + strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.width() - arrowWidth - strokeWidth, rect.top + strokeWidth); - - path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + (strokeWidth / 2)); - path.lineTo(rect.right - strokeWidth - strokeWidth, arrowPosition + arrowHeight / 2); - path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + arrowHeight - (strokeWidth / 2)); - - path.lineTo(rect.right - arrowWidth - strokeWidth, rect.bottom - strokeWidth); - - path.lineTo(rect.left + strokeWidth, rect.bottom - strokeWidth); - path.lineTo(rect.left + strokeWidth, rect.top + strokeWidth); - - path.close(); - } - - private void initRightRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.width() - cornersRadius - arrowWidth - strokeWidth, rect.top + strokeWidth); - path.arcTo(new RectF(rect.right - cornersRadius - arrowWidth, - rect.top + strokeWidth, rect.right - arrowWidth - strokeWidth, cornersRadius + rect.top), 270, 90); - - path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + (strokeWidth / 2)); - path.lineTo(rect.right - strokeWidth - strokeWidth, arrowPosition + arrowHeight / 2); - path.lineTo(rect.right - arrowWidth - strokeWidth, arrowPosition + arrowHeight - (strokeWidth / 2)); - path.lineTo(rect.right - arrowWidth - strokeWidth, rect.bottom - cornersRadius - strokeWidth); - - path.arcTo(new RectF(rect.right - cornersRadius - arrowWidth, rect.bottom - cornersRadius, - rect.right - arrowWidth - strokeWidth, rect.bottom - strokeWidth), 0, 90); - path.lineTo(rect.left + arrowWidth + strokeWidth, rect.bottom - strokeWidth); - - path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius, - cornersRadius + rect.left, rect.bottom - strokeWidth), 90, 90); - - path.arcTo(new RectF(rect.left + strokeWidth, rect.top + strokeWidth, cornersRadius - + rect.left, cornersRadius + rect.top), 180, 90); - path.close(); - } - - private void initBottomSquarePath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(rect.left + strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.right - strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.right - strokeWidth, rect.bottom - arrowHeight - strokeWidth); - - path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); - path.lineTo(rect.left + arrowPosition + arrowWidth / 2, rect.bottom - strokeWidth - strokeWidth); - path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); - path.lineTo(rect.left + arrowPosition + strokeWidth, rect.bottom - arrowHeight - strokeWidth); - - path.lineTo(rect.left + strokeWidth, rect.bottom - arrowHeight - strokeWidth); - path.lineTo(rect.left + strokeWidth, rect.top + strokeWidth); - path.close(); - } - - private void initBottomRoundedPath(@NonNull RectF rect, @NonNull Path path, float strokeWidth) { - path.moveTo(rect.left + cornersRadius + strokeWidth, rect.top + strokeWidth); - path.lineTo(rect.width() - cornersRadius - strokeWidth, rect.top + strokeWidth); - path.arcTo(new RectF(rect.right - cornersRadius, - rect.top + strokeWidth, rect.right - strokeWidth, cornersRadius + rect.top), 270, 90); - - path.lineTo(rect.right - strokeWidth, rect.bottom - arrowHeight - cornersRadius - strokeWidth); - path.arcTo(new RectF(rect.right - cornersRadius, rect.bottom - cornersRadius - arrowHeight, - rect.right - strokeWidth, rect.bottom - arrowHeight - strokeWidth), 0, 90); - - path.lineTo(rect.left + arrowWidth + arrowPosition - (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); - path.lineTo(rect.left + arrowPosition + arrowWidth / 2, rect.bottom - strokeWidth - strokeWidth); - path.lineTo(rect.left + arrowPosition + (strokeWidth / 2), rect.bottom - arrowHeight - strokeWidth); - path.lineTo(rect.left + Math.min(cornersRadius, arrowPosition) + strokeWidth, rect.bottom - arrowHeight - - strokeWidth); - - path.arcTo(new RectF(rect.left + strokeWidth, rect.bottom - cornersRadius - arrowHeight, - cornersRadius + rect.left, rect.bottom - arrowHeight - strokeWidth), 90, 90); - path.lineTo(rect.left + strokeWidth, rect.top + cornersRadius + strokeWidth); - path.arcTo(new RectF(rect.left + strokeWidth, rect.top + strokeWidth, cornersRadius - + rect.left, cornersRadius + rect.top), 180, 90); - path.close(); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java deleted file mode 100644 index f4a1484f8..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubbleLayout.java +++ /dev/null @@ -1,350 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.RectF; -import android.support.annotation.NonNull; -import android.util.AttributeSet; -import android.util.DisplayMetrics; -import android.widget.LinearLayout; - -import com.mapbox.mapboxsdk.R; - -/** - * Bubble View for Android with custom stroke width and color, arrow size, position and direction. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public class BubbleLayout extends LinearLayout { - - public static final float DEFAULT_STROKE_WIDTH = -1; - private ArrowDirection arrowDirection; - private float arrowWidth; - private float arrowHeight; - private float arrowPosition; - private float cornersRadius; - private Bubble bubble; - private int bubbleColor; - private float strokeWidth; - private int strokeColor; - - /** - * Creates an instance of bubble layout. - * - * @param context The context used to inflate this bubble layout - */ - public BubbleLayout(@NonNull Context context) { - this(context, null, 0); - } - - /** - * Creates an instance of bubble layout. - * - * @param context The context used to inflate this bubble layout - * @param attrs The attribute set to initialise this bubble layout from - */ - public BubbleLayout(@NonNull Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - /** - * Creates an instance of bubble layout. - * - * @param context The context used to inflate this bubble layout - * @param attrs The attribute set to initialise this bubble layout from - * @param defStyleAttr The default style to apply this bubble layout with - */ - public BubbleLayout(@NonNull Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - - TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.mapbox_BubbleLayout); - @ArrowDirection.Value - int location = a.getInt(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowDirection, - ArrowDirection.LEFT); - arrowDirection = new ArrowDirection(location); - arrowWidth = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowWidth, - convertDpToPixel(8, context)); - arrowHeight = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowHeight, - convertDpToPixel(8, context)); - arrowPosition = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_arrowPosition, - convertDpToPixel(12, context)); - cornersRadius = a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_cornersRadius, 0); - bubbleColor = a.getColor(R.styleable.mapbox_BubbleLayout_mapbox_bl_bubbleColor, Color.WHITE); - strokeWidth = - a.getDimension(R.styleable.mapbox_BubbleLayout_mapbox_bl_strokeWidth, DEFAULT_STROKE_WIDTH); - strokeColor = a.getColor(R.styleable.mapbox_BubbleLayout_mapbox_bl_strokeColor, Color.GRAY); - - a.recycle(); - initPadding(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - initDrawable(0, getWidth(), 0, getHeight()); - } - - @Override - protected void dispatchDraw(@NonNull Canvas canvas) { - if (bubble != null) { - bubble.draw(canvas); - } - super.dispatchDraw(canvas); - } - - static float convertDpToPixel(float dp, Context context) { - DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - return dp * (metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); - } - - /** - * Get the arrow direction. - * - * @return the arrow direction - */ - public ArrowDirection getArrowDirection() { - return arrowDirection; - } - - /** - * Set the arrow direction. - * - * @param arrowDirection The direction of the arrow - * @return this - */ - @NonNull - public BubbleLayout setArrowDirection(ArrowDirection arrowDirection) { - resetPadding(); - this.arrowDirection = arrowDirection; - initPadding(); - return this; - } - - /** - * Get the arrow width. - * - * @return the width of the arrow - */ - public float getArrowWidth() { - return arrowWidth; - } - - /** - * Set the arrow width. - * - * @param arrowWidth The width of the arrow - * @return this - */ - @NonNull - public BubbleLayout setArrowWidth(float arrowWidth) { - resetPadding(); - this.arrowWidth = arrowWidth; - initPadding(); - return this; - } - - /** - * Get the arrow height - * - * @return the height of the arrow - */ - public float getArrowHeight() { - return arrowHeight; - } - - /** - * Set the arrow height. - * - * @param arrowHeight The height of the arrow - * @return this - */ - @NonNull - public BubbleLayout setArrowHeight(float arrowHeight) { - resetPadding(); - this.arrowHeight = arrowHeight; - initPadding(); - return this; - } - - /** - * Get the arrow position. - * - * @return the arrow position - */ - public float getArrowPosition() { - return arrowPosition; - } - - /** - * Get the arrow position. - * - * @param arrowPosition The arrow position - * @return this - */ - @NonNull - public BubbleLayout setArrowPosition(float arrowPosition) { - resetPadding(); - this.arrowPosition = arrowPosition; - initPadding(); - return this; - } - - /** - * Get the corner radius - * - * @return the corner radius - */ - public float getCornersRadius() { - return cornersRadius; - } - - /** - * Set the corner radius - * - * @param cornersRadius The corner radius - * @return this - */ - @NonNull - public BubbleLayout setCornersRadius(float cornersRadius) { - this.cornersRadius = cornersRadius; - requestLayout(); - return this; - } - - /** - * Get the bubble color. - * - * @return the bubble color - */ - public int getBubbleColor() { - return bubbleColor; - } - - /** - * Set the bubble color. - * - * @param bubbleColor The buble color - * @return this - */ - @NonNull - public BubbleLayout setBubbleColor(int bubbleColor) { - this.bubbleColor = bubbleColor; - requestLayout(); - return this; - } - - /** - * Get stroke width. - * - * @return the stroke width - */ - public float getStrokeWidth() { - return strokeWidth; - } - - /** - * Set the stroke width. - * - * @param strokeWidth The stroke width - * @return this - */ - @NonNull - public BubbleLayout setStrokeWidth(float strokeWidth) { - resetPadding(); - this.strokeWidth = strokeWidth; - initPadding(); - return this; - } - - /** - * Get the stroke color. - * - * @return the stroke color - */ - public int getStrokeColor() { - return strokeColor; - } - - /** - * Set the stroke color. - * - * @param strokeColor The stroke color - * @return this - */ - @NonNull - public BubbleLayout setStrokeColor(int strokeColor) { - this.strokeColor = strokeColor; - requestLayout(); - return this; - } - - private void initPadding() { - int paddingLeft = getPaddingLeft(); - int paddingRight = getPaddingRight(); - int paddingTop = getPaddingTop(); - int paddingBottom = getPaddingBottom(); - switch (arrowDirection.getValue()) { - case ArrowDirection.LEFT: - paddingLeft += arrowWidth; - break; - case ArrowDirection.RIGHT: - paddingRight += arrowWidth; - break; - case ArrowDirection.TOP: - paddingTop += arrowHeight; - break; - case ArrowDirection.BOTTOM: - paddingBottom += arrowHeight; - break; - } - if (strokeWidth > 0) { - paddingLeft += strokeWidth; - paddingRight += strokeWidth; - paddingTop += strokeWidth; - paddingBottom += strokeWidth; - } - setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - private void initDrawable(int left, int right, int top, int bottom) { - if (right < left || bottom < top) { - return; - } - - RectF rectF = new RectF(left, top, right, bottom); - bubble = new Bubble(rectF, arrowDirection, arrowWidth, arrowHeight, arrowPosition, cornersRadius, - bubbleColor, strokeWidth, strokeColor); - } - - private void resetPadding() { - int paddingLeft = getPaddingLeft(); - int paddingRight = getPaddingRight(); - int paddingTop = getPaddingTop(); - int paddingBottom = getPaddingBottom(); - switch (arrowDirection.getValue()) { - case ArrowDirection.LEFT: - paddingLeft -= arrowWidth; - break; - case ArrowDirection.RIGHT: - paddingRight -= arrowWidth; - break; - case ArrowDirection.TOP: - paddingTop -= arrowHeight; - break; - case ArrowDirection.BOTTOM: - paddingBottom -= arrowHeight; - break; - } - if (strokeWidth > 0) { - paddingLeft -= strokeWidth; - paddingRight -= strokeWidth; - paddingTop -= strokeWidth; - paddingBottom -= strokeWidth; - } - setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java deleted file mode 100644 index 055bfe536..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/BubblePopupHelper.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.support.annotation.NonNull; -import android.view.ViewGroup; -import android.widget.PopupWindow; - -import com.mapbox.mapboxsdk.R; - -@Deprecated -class BubblePopupHelper { - - @NonNull - static PopupWindow create(@NonNull Context context, @NonNull BubbleLayout bubbleLayout) { - PopupWindow popupWindow = new PopupWindow(context); - - popupWindow.setContentView(bubbleLayout); - popupWindow.setOutsideTouchable(true); - popupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); - popupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - popupWindow.setAnimationStyle(android.R.style.Animation_Dialog); - // change background color to transparent - Drawable drawable; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - drawable = context.getDrawable(R.drawable.mapbox_popup_window_transparent); - } else { - drawable = context.getResources().getDrawable(R.drawable.mapbox_popup_window_transparent); - } - popupWindow.setBackgroundDrawable(drawable); - - return popupWindow; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java deleted file mode 100644 index 7ace11350..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Icon.java +++ /dev/null @@ -1,124 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Bitmap; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.DisplayMetrics; - -import java.nio.ByteBuffer; - -/** - * Icon is the visual representation of a Marker on a MapView. - * - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public class Icon { - - private Bitmap mBitmap; - private String mId; - - Icon(String id, Bitmap bitmap) { - mId = id; - mBitmap = bitmap; - } - - /** - * String identifier for this icon. - * - * @return String identifier for this icon. - */ - public String getId() { - return mId; - } - - /** - * Get the bitmap being used for this icon. - * - * @return The bitmap being used for the icon. - */ - public Bitmap getBitmap() { - if (mBitmap != null && mBitmap.getConfig() != Bitmap.Config.ARGB_8888) { - mBitmap = mBitmap.copy(Bitmap.Config.ARGB_8888, false); - } - return mBitmap; - } - - /** - * Get the icon bitmap scale. - *

- * Requires the bitmap to be set before calling this method. - *

- * - * @return the scale of the bitmap - */ - public float getScale() { - if (mBitmap == null) { - throw new IllegalStateException("Required to set a Icon before calling getScale"); - } - float density = mBitmap.getDensity(); - if (density == Bitmap.DENSITY_NONE) { - density = DisplayMetrics.DENSITY_DEFAULT; - } - return density / DisplayMetrics.DENSITY_DEFAULT; - } - - /** - * Get the icon bitmap bytes. - *

- * Requires the bitmap to be set before calling this method. - *

- * - * @return the bytes of the bitmap - */ - @NonNull - public byte[] toBytes() { - if (mBitmap == null) { - throw new IllegalStateException("Required to set a Icon before calling toBytes"); - } - ByteBuffer buffer = ByteBuffer.allocate(mBitmap.getRowBytes() * mBitmap.getHeight()); - mBitmap.copyPixelsToBuffer(buffer); - return buffer.array(); - } - - /** - * Compares this icon object with another icon and determines if they match. - * - * @param object Another iconi to compare with this object. - * @return True if the icon being passed in matches this icon object. Else, false. - */ - @Override - public boolean equals(@Nullable Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - - Icon icon = (Icon) object; - return mBitmap.equals(icon.mBitmap) && mId.equals(icon.mId); - } - - /** - * Gives an integer which can be used as the bucket number for storing elements of the set/map. - * This bucket number is the address of the element inside the set/map. There's no guarantee - * that this hash value will be consistent between different Java implementations, or even - * between different execution runs of the same program. - * - * @return integer value you can use for storing element. - */ - @Override - public int hashCode() { - int result = 0; - if (mBitmap != null) { - result = mBitmap.hashCode(); - } - if (mId != null) { - result = 31 * result + mId.hashCode(); - } - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java deleted file mode 100644 index 9f0fa62cd..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/IconFactory.java +++ /dev/null @@ -1,188 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Build; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.util.DisplayMetrics; -import android.view.WindowManager; - -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.exceptions.TooManyIconsException; -import com.mapbox.mapboxsdk.utils.BitmapUtils; - -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; - -/** - * Factory for creating Icons from bitmap images. - *

- * icon is used to display bitmaps on top of the map using {@link Marker}. - *

- * - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public final class IconFactory { - - private static final String ICON_ID_PREFIX = "com.mapbox.icons.icon_"; - - private Context context; - @SuppressLint("StaticFieldLeak") - private static IconFactory instance; - private Icon defaultMarker; - private BitmapFactory.Options options; - - private int nextId = 0; - - /** - * Get a single instance of IconFactory. - * - * @param context the context to derive the application context from - * @return the single instance of IconFactory - */ - public static synchronized IconFactory getInstance(@NonNull Context context) { - if (instance == null) { - instance = new IconFactory(context.getApplicationContext()); - } - return instance; - } - - private IconFactory(@NonNull Context context) { - this.context = context; - DisplayMetrics realMetrics = null; - DisplayMetrics metrics = new DisplayMetrics(); - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - realMetrics = new DisplayMetrics(); - wm.getDefaultDisplay().getRealMetrics(realMetrics); - } - wm.getDefaultDisplay().getMetrics(metrics); - - options = new BitmapFactory.Options(); - options.inScaled = true; - options.inDensity = DisplayMetrics.DENSITY_DEFAULT; - options.inTargetDensity = metrics.densityDpi; - if (realMetrics != null) { - options.inScreenDensity = realMetrics.densityDpi; - } - } - - /** - * Creates an icon from a given Bitmap image. - * - * @param bitmap image used for creating the Icon. - * @return The icon using the given Bitmap image. - */ - public Icon fromBitmap(@NonNull Bitmap bitmap) { - if (nextId < 0) { - throw new TooManyIconsException(); - } - String id = ICON_ID_PREFIX + ++nextId; - return new Icon(id, bitmap); - } - - /** - * Creates an icon using the resource ID of a Bitmap image. - * - * @param resourceId The resource ID of a Bitmap image. - * @return The icon that was loaded from the asset or {@code null} if failed to load. - */ - public Icon fromResource(@DrawableRes int resourceId) { - Drawable drawable = BitmapUtils.getDrawableFromRes(context, resourceId); - if (drawable instanceof BitmapDrawable) { - BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; - return fromBitmap(bitmapDrawable.getBitmap()); - } else { - throw new IllegalArgumentException("Failed to decode image. The resource provided must be a Bitmap."); - } - } - - /** - * Provides an icon using the default marker icon used for {@link Marker}. - * - * @return An icon with the default {@link Marker} icon. - */ - public Icon defaultMarker() { - if (defaultMarker == null) { - defaultMarker = fromResource(R.drawable.mapbox_marker_icon_default); - } - return defaultMarker; - } - - private Icon fromInputStream(@NonNull InputStream is) { - Bitmap bitmap = BitmapFactory.decodeStream(is, null, options); - return fromBitmap(bitmap); - } - - /** - * Creates an Icon using the name of a Bitmap image in the assets directory. - * - * @param assetName The name of a Bitmap image in the assets directory. - * @return The Icon that was loaded from the asset or null if failed to load. - */ - public Icon fromAsset(@NonNull String assetName) { - InputStream is; - try { - is = context.getAssets().open(assetName); - } catch (IOException ioException) { - MapStrictMode.strictModeViolation(ioException); - return null; - } - return fromInputStream(is); - } - - /** - * Creates an Icon using the absolute file path of a Bitmap image. - * - * @param absolutePath The absolute path of the Bitmap image. - * @return The Icon that was loaded from the absolute path or null if failed to load. - */ - public Icon fromPath(@NonNull String absolutePath) { - Bitmap bitmap = BitmapFactory.decodeFile(absolutePath, options); - return fromBitmap(bitmap); - } - - /** - * Create an Icon using the name of a Bitmap image file located in the internal storage. - * In particular, this calls Context#openFileInput(String). - * - * @param fileName The name of the Bitmap image file. - * @return The Icon that was loaded from the asset or null if failed to load. - * @see - * Using the Internal Storage - */ - public Icon fromFile(@NonNull String fileName) { - FileInputStream is; - try { - is = context.openFileInput(fileName); - } catch (FileNotFoundException fileNotFoundException) { - MapStrictMode.strictModeViolation(fileNotFoundException); - return null; - } - return fromInputStream(is); - } - - /** - * Create an Icon using a previously created icon identifier along with a provided Bitmap. - * - * @param iconId The Icon identifier you'd like to recreate. - * @param bitmap a Bitmap used to replace the current one. - * @return The Icon using the new Bitmap. - */ - public static Icon recreate(@NonNull String iconId, @NonNull Bitmap bitmap) { - return new Icon(iconId, bitmap); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java deleted file mode 100644 index 5bd9aaef1..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/InfoWindow.java +++ /dev/null @@ -1,351 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.content.res.Resources; -import android.graphics.PointF; -import android.os.Build; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.widget.TextView; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import java.lang.ref.WeakReference; - -/** - * {@code InfoWindow} is a tooltip shown when a {@link Marker} is tapped. Only - * one info window is displayed at a time. When the user clicks on a marker, the currently open info - * window will be closed and the new info window will be displayed. If the user clicks the same - * marker while its info window is currently open, the info window will be closed. - *

- * The info window is drawn oriented against the device's screen, centered above its associated - * marker by default. The default info window contains the title in bold and snippet text below the title. - * While either the title and - * snippet are optional, at least one is required to open the info window. - *

- * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public class InfoWindow { - - private WeakReference boundMarker; - private WeakReference mapboxMap; - protected WeakReference view; - - private float markerHeightOffset; - private float markerWidthOffset; - private float viewWidthOffset; - private float viewHeightOffset; - private PointF coordinates; - private boolean isVisible; - - @LayoutRes - private int layoutRes; - - InfoWindow(MapView mapView, int layoutResId, MapboxMap mapboxMap) { - layoutRes = layoutResId; - View view = LayoutInflater.from(mapView.getContext()).inflate(layoutResId, mapView, false); - initialize(view, mapboxMap); - } - - InfoWindow(@NonNull View view, MapboxMap mapboxMap) { - initialize(view, mapboxMap); - } - - private void initialize(@NonNull View view, MapboxMap mapboxMap) { - this.mapboxMap = new WeakReference<>(mapboxMap); - isVisible = false; - this.view = new WeakReference<>(view); - - view.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - MapboxMap mapboxMap = InfoWindow.this.mapboxMap.get(); - if (mapboxMap != null) { - MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener = mapboxMap.getOnInfoWindowClickListener(); - boolean handledDefaultClick = false; - if (onInfoWindowClickListener != null) { - handledDefaultClick = onInfoWindowClickListener.onInfoWindowClick(getBoundMarker()); - } - - if (!handledDefaultClick) { - // default behavior: close it when clicking on the tooltip: - closeInfoWindow(); - } - } - } - }); - - view.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - MapboxMap mapboxMap = InfoWindow.this.mapboxMap.get(); - if (mapboxMap != null) { - MapboxMap.OnInfoWindowLongClickListener listener = mapboxMap.getOnInfoWindowLongClickListener(); - if (listener != null) { - listener.onInfoWindowLongClick(getBoundMarker()); - } - } - return true; - } - }); - } - - private void closeInfoWindow() { - MapboxMap mapbox = mapboxMap.get(); - Marker marker = boundMarker.get(); - if (marker != null && mapbox != null) { - mapbox.deselectMarker(marker); - } - close(); - } - - /** - * Open the info window at the specified position. - * - * @param boundMarker The marker on which is hooked the view. - * @param position to place the window on the map. - * @param offsetX The offset of the view to the position, in pixels. This allows to offset - * the view from the object position. - * @param offsetY The offset of the view to the position, in pixels. This allows to offset - * the view from the object position. - * @return this {@link InfoWindow}. - */ - @NonNull - InfoWindow open(@NonNull MapView mapView, Marker boundMarker, @NonNull LatLng position, int offsetX, int offsetY) { - setBoundMarker(boundMarker); - - MapView.LayoutParams lp = new MapView.LayoutParams(MapView.LayoutParams.WRAP_CONTENT, - MapView.LayoutParams.WRAP_CONTENT); - - MapboxMap mapboxMap = this.mapboxMap.get(); - View view = this.view.get(); - if (view != null && mapboxMap != null) { - view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); - - markerHeightOffset = offsetY; - markerWidthOffset = -offsetX; - - // Calculate default Android x,y coordinate - coordinates = mapboxMap.getProjection().toScreenLocation(position); - float x = coordinates.x - (view.getMeasuredWidth() / 2) + offsetX; - float y = coordinates.y - view.getMeasuredHeight() + offsetY; - - if (view instanceof BubbleLayout) { - // only apply repositioning/margin for InfoWindowView - Resources resources = mapView.getContext().getResources(); - - // get right/left popup window - float rightSideInfowWindow = x + view.getMeasuredWidth(); - float leftSideInfoWindow = x; - - // get right/left map view - final float mapRight = mapView.getRight(); - final float mapLeft = mapView.getLeft(); - - float marginHorizontal = resources.getDimension(R.dimen.mapbox_infowindow_margin); - float tipViewOffset = resources.getDimension(R.dimen.mapbox_infowindow_tipview_width) / 2; - float tipViewMarginLeft = view.getMeasuredWidth() / 2 - tipViewOffset; - - boolean outOfBoundsLeft = false; - boolean outOfBoundsRight = false; - - // only optimise margins if view is inside current viewport - if (coordinates.x >= 0 && coordinates.x <= mapView.getWidth() - && coordinates.y >= 0 && coordinates.y <= mapView.getHeight()) { - - // if out of bounds right - if (rightSideInfowWindow > mapRight) { - outOfBoundsRight = true; - x -= rightSideInfowWindow - mapRight; - tipViewMarginLeft += rightSideInfowWindow - mapRight + tipViewOffset; - rightSideInfowWindow = x + view.getMeasuredWidth(); - } - - // fit screen left - if (leftSideInfoWindow < mapLeft) { - outOfBoundsLeft = true; - x += mapLeft - leftSideInfoWindow; - tipViewMarginLeft -= mapLeft - leftSideInfoWindow + tipViewOffset; - leftSideInfoWindow = x; - } - - // Add margin right - if (outOfBoundsRight && mapRight - rightSideInfowWindow < marginHorizontal) { - x -= marginHorizontal - (mapRight - rightSideInfowWindow); - tipViewMarginLeft += marginHorizontal - (mapRight - rightSideInfowWindow) - tipViewOffset; - leftSideInfoWindow = x; - } - - // Add margin left - if (outOfBoundsLeft && leftSideInfoWindow - mapLeft < marginHorizontal) { - x += marginHorizontal - (leftSideInfoWindow - mapLeft); - tipViewMarginLeft -= (marginHorizontal - (leftSideInfoWindow - mapLeft)) - tipViewOffset; - } - } - - // Adjust tipView - ((BubbleLayout) view).setArrowPosition(tipViewMarginLeft); - } - - // set anchor popupwindowview - view.setX(x); - view.setY(y); - - // Calculate x-offset and y-offset for update method - viewWidthOffset = x - coordinates.x - offsetX; - viewHeightOffset = -view.getMeasuredHeight() + offsetY; - - - close(); // if it was already opened - mapView.addView(view, lp); - isVisible = true; - } - return this; - } - - /** - * Close this {@link InfoWindow} if it is visible, otherwise calling this will do nothing. - * - * @return This {@link InfoWindow} - */ - @NonNull - InfoWindow close() { - MapboxMap mapboxMap = this.mapboxMap.get(); - if (isVisible && mapboxMap != null) { - isVisible = false; - View view = this.view.get(); - if (view != null && view.getParent() != null) { - ((ViewGroup) view.getParent()).removeView(view); - } - - Marker marker = getBoundMarker(); - MapboxMap.OnInfoWindowCloseListener listener = mapboxMap.getOnInfoWindowCloseListener(); - if (listener != null) { - listener.onInfoWindowClose(marker); - } - - setBoundMarker(null); - } - return this; - } - - /** - * Constructs the view that is displayed when the InfoWindow opens. This retrieves data from - * overlayItem and shows it in the tooltip. - * - * @param overlayItem the tapped overlay item - */ - void adaptDefaultMarker(@NonNull Marker overlayItem, MapboxMap mapboxMap, @NonNull MapView mapView) { - View view = this.view.get(); - if (view == null) { - view = LayoutInflater.from(mapView.getContext()).inflate(layoutRes, mapView, false); - initialize(view, mapboxMap); - } - this.mapboxMap = new WeakReference<>(mapboxMap); - String title = overlayItem.getTitle(); - TextView titleTextView = ((TextView) view.findViewById(R.id.infowindow_title)); - if (!TextUtils.isEmpty(title)) { - titleTextView.setText(title); - titleTextView.setVisibility(View.VISIBLE); - } else { - titleTextView.setVisibility(View.GONE); - } - - String snippet = overlayItem.getSnippet(); - TextView snippetTextView = ((TextView) view.findViewById(R.id.infowindow_description)); - if (!TextUtils.isEmpty(snippet)) { - snippetTextView.setText(snippet); - snippetTextView.setVisibility(View.VISIBLE); - } else { - snippetTextView.setVisibility(View.GONE); - } - } - - @NonNull - InfoWindow setBoundMarker(Marker boundMarker) { - this.boundMarker = new WeakReference<>(boundMarker); - return this; - } - - @Nullable - Marker getBoundMarker() { - if (boundMarker == null) { - return null; - } - return boundMarker.get(); - } - - /** - * Will result in getting this {@link InfoWindow} and updating the view being displayed. - */ - public void update() { - MapboxMap mapboxMap = this.mapboxMap.get(); - Marker marker = boundMarker.get(); - View view = this.view.get(); - if (mapboxMap != null && marker != null && view != null) { - coordinates = mapboxMap.getProjection().toScreenLocation(marker.getPosition()); - - if (view instanceof BubbleLayout) { - view.setX(coordinates.x + viewWidthOffset - markerWidthOffset); - } else { - view.setX(coordinates.x - (view.getMeasuredWidth() / 2) - markerWidthOffset); - } - view.setY(coordinates.y + viewHeightOffset); - } - } - - void onContentUpdate() { - //recalculate y-offset and update position - View view = this.view.get(); - if (view != null) { - ViewTreeObserver viewTreeObserver = view.getViewTreeObserver(); - if (viewTreeObserver.isAlive()) { - viewTreeObserver.addOnGlobalLayoutListener(contentUpdateListener); - } - } - } - - @Nullable - private final ViewTreeObserver.OnGlobalLayoutListener contentUpdateListener = - new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - View view = InfoWindow.this.view.get(); - if (view != null) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - view.getViewTreeObserver().removeOnGlobalLayoutListener(this); - } else { - view.getViewTreeObserver().removeGlobalOnLayoutListener(this); - } - viewHeightOffset = -view.getMeasuredHeight() + markerHeightOffset; - update(); - } - } - }; - - /** - * Retrieve this {@link InfoWindow}'s current view being used. - * - * @return This {@link InfoWindow}'s current View. - */ - @Nullable - public View getView() { - return view != null ? view.get() : null; - } - - boolean isVisible() { - return isVisible; - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java deleted file mode 100644 index c26af9436..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Marker.java +++ /dev/null @@ -1,278 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.View; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -/** - * Marker is an annotation that shows an icon image at a geographical location. The default marker - * uses a provided icon. This icon can be customized using {@link IconFactory} to generate an - * {@link Icon} using a provided image. Markers are added to the map by first giving a - * {@link LatLng} and using {@link MapboxMap#addMarker(MarkerOptions)}. The marker icon will be - * centered at this position so it is common to add padding to the icon image before usage. - *

- * Markers are designed to be interactive. They receive click events by default, and are often used - * with event listeners to bring up info windows. An {@link InfoWindow} is displayed by default when - * either a title or snippet is provided. - *

- * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public class Marker extends Annotation { - - @Keep - private LatLng position; - private String snippet; - @Nullable - private Icon icon; - //Redundantly stored for JNI access - @Nullable - @Keep - private String iconId; - private String title; - - @Nullable - private InfoWindow infoWindow; - private boolean infoWindowShown; - - private int topOffsetPixels; - private int rightOffsetPixels; - - /** - * Constructor - */ - Marker() { - super(); - } - - /** - * Creates a instance of {@link Marker} using the builder of Marker. - * - * @param baseMarkerOptions The builder used to construct the Marker. - */ - public Marker(BaseMarkerOptions baseMarkerOptions) { - this(baseMarkerOptions.position, baseMarkerOptions.icon, baseMarkerOptions.title, baseMarkerOptions.snippet); - } - - Marker(LatLng position, Icon icon, String title, String snippet) { - this.position = position; - this.title = title; - this.snippet = snippet; - setIcon(icon); - } - - /** - * Returns the position of the marker. - * - * @return A {@link LatLng} object specifying the marker's current position. - */ - public LatLng getPosition() { - return position; - } - - /** - * Gets the snippet of the marker. - * - * @return A string containing the marker's snippet. - */ - public String getSnippet() { - return snippet; - } - - /** - * Gets the snippet of the marker. - * - * @return A string containing the marker's snippet. - */ - public String getTitle() { - return title; - } - - /** - * Do not use this method, used internally by the SDK. - */ - public void hideInfoWindow() { - if (infoWindow != null) { - infoWindow.close(); - } - infoWindowShown = false; - } - - /** - * Do not use this method, used internally by the SDK. - * - * @return true if the infoWindow is shown - */ - public boolean isInfoWindowShown() { - return infoWindowShown; - } - - /** - * Sets the location of the marker. - * - * @param position A {@link LatLng} defining the marker position. - */ - public void setPosition(LatLng position) { - this.position = position; - MapboxMap map = getMapboxMap(); - if (map != null) { - map.updateMarker(this); - } - } - - /** - * Sets the snippet of the marker. - * - * @param snippet A String used in the marker info window. If {@code null}, the snippet is - * cleared. - */ - public void setSnippet(String snippet) { - this.snippet = snippet; - refreshInfoWindowContent(); - } - - /** - * Sets the icon of the marker. - * - * @param icon The {@link Icon} to be used as Marker image - */ - public void setIcon(@Nullable Icon icon) { - this.icon = icon; - this.iconId = icon != null ? icon.getId() : null; - MapboxMap map = getMapboxMap(); - if (map != null) { - map.updateMarker(this); - } - } - - /** - * Gets the {@link Icon} currently used for the marker. If no Icon was set for the marker, the - * default icon will be returned. - * - * @return The {@link Icon} the marker is using. - */ - @Nullable - public Icon getIcon() { - return icon; - } - - /** - * Sets the title of the marker. - * - * @param title A String used in the marker info window. If {@code null}, the title is - * cleared. - */ - public void setTitle(String title) { - this.title = title; - refreshInfoWindowContent(); - } - - /** - * Gets the {@link InfoWindow} the marker is using. If the marker hasn't had an info window - * defined, this will return {@code null}. - * - * @return The info window the marker is using. - */ - @Nullable - public InfoWindow getInfoWindow() { - return infoWindow; - } - - /** - * Update only for default Marker's InfoWindow content for Title and Snippet - */ - private void refreshInfoWindowContent() { - if (isInfoWindowShown() && mapView != null && mapboxMap != null && mapboxMap.getInfoWindowAdapter() == null) { - InfoWindow infoWindow = getInfoWindow(mapView); - if (mapView.getContext() != null) { - infoWindow.adaptDefaultMarker(this, mapboxMap, mapView); - } - MapboxMap map = getMapboxMap(); - if (map != null) { - map.updateMarker(this); - } - infoWindow.onContentUpdate(); - } - } - - /** - * Do not use this method, used internally by the SDK. Use {@link MapboxMap#selectMarker(Marker)} - * if you want to programmatically display the markers info window. - * - * @param mapboxMap The hosting mapbox map. - * @param mapView The hosting map view. - * @return The info window that was shown. - */ - @Nullable - public InfoWindow showInfoWindow(@NonNull MapboxMap mapboxMap, @NonNull MapView mapView) { - setMapboxMap(mapboxMap); - setMapView(mapView); - MapboxMap.InfoWindowAdapter infoWindowAdapter = getMapboxMap().getInfoWindowAdapter(); - if (infoWindowAdapter != null) { - // end developer is using a custom InfoWindowAdapter - View content = infoWindowAdapter.getInfoWindow(this); - if (content != null) { - infoWindow = new InfoWindow(content, mapboxMap); - showInfoWindow(infoWindow, mapView); - return infoWindow; - } - } - - InfoWindow infoWindow = getInfoWindow(mapView); - if (mapView.getContext() != null) { - infoWindow.adaptDefaultMarker(this, mapboxMap, mapView); - } - return showInfoWindow(infoWindow, mapView); - } - - @NonNull - private InfoWindow showInfoWindow(InfoWindow iw, MapView mapView) { - iw.open(mapView, this, getPosition(), rightOffsetPixels, topOffsetPixels); - infoWindowShown = true; - return iw; - } - - @Nullable - private InfoWindow getInfoWindow(@NonNull MapView mapView) { - if (infoWindow == null && mapView.getContext() != null) { - infoWindow = new InfoWindow(mapView, R.layout.mapbox_infowindow_content, getMapboxMap()); - } - return infoWindow; - } - - /** - * Do not use this method, used internally by the SDK. - * - * @param topOffsetPixels the top offset pixels. - */ - public void setTopOffsetPixels(int topOffsetPixels) { - this.topOffsetPixels = topOffsetPixels; - } - - /** - * Do not use this method, used internally by the SDK. - * - * @param rightOffsetPixels the right offset pixels. - */ - public void setRightOffsetPixels(int rightOffsetPixels) { - this.rightOffsetPixels = rightOffsetPixels; - } - - /** - * Returns a String with the marker position. - * - * @return A String with the marker position. - */ - @Override - public String toString() { - return "Marker [position[" + getPosition() + "]]"; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java deleted file mode 100644 index 62c3d378e..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/MarkerOptions.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Bitmap; -import android.os.Parcel; -import android.os.Parcelable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException; -import com.mapbox.mapboxsdk.geometry.LatLng; - -/** - *

- * Builder for composing {@link Marker} objects. See {@link Marker} for additional information. - *

- *

Example

- *
- * mapView.addMarker(new MarkerOptions()
- *   .title("Intersection")
- *   .snippet("H St NW with 15th St NW")
- *   .position(new LatLng(38.9002073, -77.03364419)));
- * 
- * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public final class MarkerOptions extends BaseMarkerOptions implements Parcelable { - - /** - * Defines options for a Marker. - */ - public MarkerOptions() { - } - - protected MarkerOptions(Parcel in) { - position((LatLng) in.readParcelable(LatLng.class.getClassLoader())); - snippet(in.readString()); - title(in.readString()); - if (in.readByte() != 0) { - // this means we have an icon - String iconId = in.readString(); - Bitmap iconBitmap = in.readParcelable(Bitmap.class.getClassLoader()); - Icon icon = new Icon(iconId, iconBitmap); - icon(icon); - } - } - - @NonNull - @Override - public MarkerOptions getThis() { - return this; - } - - /** - * Describe the kinds of special objects contained in this Parcelable's - * marshalled representation. - * - * @return integer 0. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. May be 0 or - * {@link #PARCELABLE_WRITE_RETURN_VALUE}. - */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeParcelable(getPosition(), flags); - out.writeString(getSnippet()); - out.writeString(getTitle()); - Icon icon = getIcon(); - out.writeByte((byte) (icon != null ? 1 : 0)); - if (icon != null) { - out.writeString(getIcon().getId()); - out.writeParcelable(getIcon().getBitmap(), flags); - } - } - - /** - * Do not use this method. Used internally by the SDK. - * - * @return Marker The build marker - */ - public Marker getMarker() { - if (position == null) { - throw new InvalidMarkerPositionException(); - } - - return new Marker(position, icon, title, snippet); - } - - /** - * Returns the position set for this {@link MarkerOptions} object. - * - * @return A {@link LatLng} object specifying the marker's current position. - */ - public LatLng getPosition() { - return position; - } - - /** - * Gets the snippet set for this {@link MarkerOptions} object. - * - * @return A string containing the marker's snippet. - */ - public String getSnippet() { - return snippet; - } - - /** - * Gets the title set for this {@link MarkerOptions} object. - * - * @return A string containing the marker's title. - */ - public String getTitle() { - return title; - } - - /** - * Gets the custom icon set for this {@link MarkerOptions} object. - * - * @return A {@link Icon} object that the marker is using. If the icon wasn't set, default icon - * will return. - */ - public Icon getIcon() { - return icon; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public MarkerOptions createFromParcel(@NonNull Parcel in) { - return new MarkerOptions(in); - } - - public MarkerOptions[] newArray(int size) { - return new MarkerOptions[size]; - } - }; - - /** - * Compares this {@link MarkerOptions} object with another {@link MarkerOptions} and - * determines if their properties match. - * - * @param o Another {@link MarkerOptions} to compare with this object. - * @return True if marker properties match this {@link MarkerOptions} object. - * Else, false. - */ - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - MarkerOptions marker = (MarkerOptions) o; - - if (getPosition() != null ? !getPosition().equals(marker.getPosition()) : marker.getPosition() != null) { - return false; - } - if (getSnippet() != null ? !getSnippet().equals(marker.getSnippet()) : marker.getSnippet() != null) { - return false; - } - if (getIcon() != null ? !getIcon().equals(marker.getIcon()) : marker.getIcon() != null) { - return false; - } - return !(getTitle() != null ? !getTitle().equals(marker.getTitle()) : marker.getTitle() != null); - } - - /** - * Gives an integer which can be used as the bucket number for storing elements of the set/map. - * This bucket number is the address of the element inside the set/map. There's no guarantee - * that this hash value will be consistent between different Java implementations, or even - * between different execution runs of the same program. - * - * @return integer value you can use for storing element. - */ - @Override - public int hashCode() { - int result = 1; - result = 31 * result + (getPosition() != null ? getPosition().hashCode() : 0); - result = 31 * result + (getSnippet() != null ? getSnippet().hashCode() : 0); - result = 31 * result + (getIcon() != null ? getIcon().hashCode() : 0); - result = 31 * result + (getTitle() != null ? getTitle().hashCode() : 0); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java deleted file mode 100644 index 5f6cef8d4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polygon.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Color; -import android.support.annotation.Keep; - -import android.support.annotation.NonNull; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import java.util.ArrayList; -import java.util.List; - -/** - * Polygon is a geometry annotation that's a closed loop of coordinates. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public final class Polygon extends BasePointCollection { - - @Keep - private int fillColor = Color.BLACK; // default fillColor is black - @Keep - private int strokeColor = Color.BLACK; // default strokeColor is black - @Keep - private List> holes; - - Polygon() { - super(); - holes = new ArrayList<>(); - } - - /** - * Get the color of the fill region of the polygon. - * - * @return The color of the fill. - */ - public int getFillColor() { - return fillColor; - } - - /** - * Get the color of the stroke of the polygon. - * - * @return The color of the stroke. - */ - public int getStrokeColor() { - return strokeColor; - } - - /** - * Returns a copy of the holes. - * - * @return A {@link List} of {@link List} of {@link LatLng} points making up the holes. - */ - public List> getHoles() { - return new ArrayList<>(holes); - } - - /** - * Sets the color of the fill region of the polygon. - * - * @param color The color in ARGB format. - */ - public void setFillColor(int color) { - fillColor = color; - update(); - } - - /** - * Sets the color of the stroke of the polygon. - * - * @param color The color in ARGB format. - */ - public void setStrokeColor(int color) { - strokeColor = color; - update(); - } - - /** - * Sets the holes of this polygon. This method will take a copy of the holes, so further - * mutations to holes will have no effect on this polygon. - * - * @param holes A {@link List} of {@link List} of {@link LatLng} points making up the holes. - */ - public void setHoles(@NonNull List> holes) { - this.holes = new ArrayList<>(holes); - update(); - } - - /** - * Add a hole to the polygon. - * - * @param hole A {@link List} of {@link List} of {@link LatLng} points making up the hole to be added. - */ - void addHole(List hole) { - holes.add(hole); - update(); - } - - @Override - void update() { - MapboxMap mapboxMap = getMapboxMap(); - if (mapboxMap != null) { - mapboxMap.updatePolygon(this); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolygonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolygonOptions.java deleted file mode 100644 index 452420fcb..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolygonOptions.java +++ /dev/null @@ -1,308 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - - -import android.os.Parcel; -import android.os.Parcelable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.util.ArrayList; -import java.util.List; - -/** - * Builder for composing {@link Polygon} objects. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public final class PolygonOptions implements Parcelable { - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public PolygonOptions createFromParcel(@NonNull Parcel in) { - return new PolygonOptions(in); - } - - public PolygonOptions[] newArray(int size) { - return new PolygonOptions[size]; - } - }; - - private PolygonOptions(Parcel in) { - polygon = new Polygon(); - List pointsList = new ArrayList<>(); - in.readList(pointsList, LatLng.class.getClassLoader()); - addAll(pointsList); - List> holes = new ArrayList<>(); - in.readList(holes, LatLng.class.getClassLoader()); - addAllHoles(holes); - alpha(in.readFloat()); - fillColor(in.readInt()); - strokeColor(in.readInt()); - } - - /** - * Describe the kinds of special objects contained in this Parcelable's - * marshalled representation. - * - * @return integer 0. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. May be 0 or - * {@link #PARCELABLE_WRITE_RETURN_VALUE}. - */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeList(getPoints()); - out.writeList(getHoles()); - out.writeFloat(getAlpha()); - out.writeInt(getFillColor()); - out.writeInt(getStrokeColor()); - } - - private Polygon polygon; - - /** - * Defines options for a polygon. - */ - public PolygonOptions() { - polygon = new Polygon(); - } - - /** - * Adds a vertex to the outline of the polygon being built. - * - * @param point {@link LatLng} point to be added to polygon geometry. - * @return This {@link PolygonOptions} object with the given point added to the outline. - */ - @NonNull - public PolygonOptions add(LatLng point) { - polygon.addPoint(point); - return this; - } - - /** - * Adds vertices to the outline of the polygon being built. - * - * @param points {@link LatLng} points to be added to polygon geometry. - * @return This {@link PolygonOptions} object with the given points added to the outline. - */ - @NonNull - public PolygonOptions add(LatLng... points) { - for (LatLng point : points) { - add(point); - } - return this; - } - - /** - * Adds vertices to the outline of the polygon being built. - * - * @param points {@link Iterable} list made up of {@link LatLng} points defining the polygon - * geometry - * @return This {@link PolygonOptions} object with the given points added to the outline. - */ - @NonNull - public PolygonOptions addAll(Iterable points) { - for (LatLng point : points) { - add(point); - } - return this; - } - - /** - * Adds a hole to the outline of the polygon being built. - * - * @param hole {@link List} list made up of {@link LatLng} points defining the hole - * @return This {@link PolygonOptions} object with the given hole added to the outline. - */ - @NonNull - public PolygonOptions addHole(List hole) { - polygon.addHole(hole); - return this; - } - - /** - * Adds holes to the outline of the polygon being built. - * - * @param holes {@link List} list made up of {@link LatLng} holes to be added to polygon geometry - * @return This {@link PolygonOptions} object with the given holes added to the outline. - */ - @NonNull - public PolygonOptions addHole(List... holes) { - for (List hole : holes) { - addHole(hole); - } - return this; - } - - /** - * Adds holes to the outline of the polygon being built. - * - * @param holes {@link Iterable} list made up of {@link List} list of {@link LatLng} holes defining the hole geometry - * @return This {@link PolygonOptions} object with the given holes added to the outline. - */ - @NonNull - public PolygonOptions addAllHoles(Iterable> holes) { - for (List hole : holes) { - addHole(hole); - } - return this; - } - - /** - * Set the alpha value of the polyline. - * - * @param alpha float value between 0 (not visible) and 1. - * @return This {@link PolygonOptions} object with the given polygon alpha value. - */ - @NonNull - public PolygonOptions alpha(float alpha) { - polygon.setAlpha(alpha); - return this; - } - - /** - * Gets the alpha set for this {@link PolygonOptions} object. - * - * @return float value between 0 and 1 defining the alpha. - */ - public float getAlpha() { - return polygon.getAlpha(); - } - - /** - * Specifies the polygon's fill color, as 32-bit ARGB. The default color is black. - * - * @param color 32-bit ARGB color. - * @return This {@link PolylineOptions} object with a new color set. - */ - @NonNull - public PolygonOptions fillColor(int color) { - polygon.setFillColor(color); - return this; - } - - /** - * Gets the fill color set for this {@link PolygonOptions} object. - * - * @return The fill color of the polygon in ARGB format. - */ - public int getFillColor() { - return polygon.getFillColor(); - } - - /** - * Do not use this method. Used internally by the SDK. - * - * @return Polygon the Polygon to return - */ - public Polygon getPolygon() { - return polygon; - } - - /** - * Specifies the polygon's stroke color, as 32-bit ARGB. The default color is black. - * - * @param color 32-bit ARGB color. - * @return This {@link PolygonOptions} object with a new stroke color set. - */ - @NonNull - public PolygonOptions strokeColor(int color) { - polygon.setStrokeColor(color); - return this; - } - - /** - * Gets the stroke color set for this {@link PolygonOptions} object. - * - * @return The stroke color of the polygon in ARGB format. - */ - public int getStrokeColor() { - return polygon.getStrokeColor(); - } - - /** - * Gets the points set for this {@link PolygonOptions} object. - * - * @return The list made up of {@link LatLng} points defining the polygon. - */ - public List getPoints() { - // the getter gives us a copy, which is the safe thing to do... - return polygon.getPoints(); - } - - /** - * Gets the holes set for this {@link PolygonOptions} object. - * - * @return The list made up of {@link List} of {@link List} of {@link LatLng} points defining the holes. - */ - public List> getHoles() { - return polygon.getHoles(); - } - - - /** - * Compares this {@link PolygonOptions} object with another {@link PolygonOptions} and - * determines if their color, alpha, stroke color, and vertices match. - * - * @param o Another {@link PolygonOptions} to compare with this object. - * @return True if color, alpha, stroke color, vertices and holes match this {@link PolygonOptions} - * {@link PolygonOptions} object. Else, false. - */ - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PolygonOptions polygon = (PolygonOptions) o; - - if (Float.compare(polygon.getAlpha(), getAlpha()) != 0) { - return false; - } - if (getFillColor() != polygon.getFillColor()) { - return false; - } - if (getStrokeColor() != polygon.getStrokeColor()) { - return false; - } - if (getPoints() != null ? !getPoints().equals(polygon.getPoints()) : polygon.getPoints() != null) { - return false; - } - return !(getHoles() != null ? !getHoles().equals(polygon.getHoles()) : polygon.getHoles() != null); - } - - /** - * Gives an integer which can be used as the bucket number for storing elements of the set/map. - * This bucket number is the address of the element inside the set/map. There's no guarantee - * that this hash value will be consistent between different Java implementations, or even - * between different execution runs of the same program. - * - * @return integer value you can use for storing element. - */ - @Override - public int hashCode() { - int result = 1; - result = 31 * result + (getAlpha() != +0.0f ? Float.floatToIntBits(getAlpha()) : 0); - result = 31 * result + getFillColor(); - result = 31 * result + getStrokeColor(); - result = 31 * result + (getPoints() != null ? getPoints().hashCode() : 0); - result = 31 * result + (getHoles() != null ? getHoles().hashCode() : 0); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java deleted file mode 100644 index 76ea15215..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/Polyline.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Color; -import android.support.annotation.Keep; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -/** - * Polyline is a geometry feature with an unclosed list of coordinates drawn as a line - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public final class Polyline extends BasePointCollection { - - @Keep - private int color = Color.BLACK; // default color is black - @Keep - private float width = 10; // As specified by Google API Docs (in pixels) - - Polyline() { - super(); - } - - /** - * Gets the color of this polyline. - * - * @return The color in ARGB format. - */ - public int getColor() { - return color; - } - - /** - * Gets the width of this polyline. - * - * @return The width in screen pixels. - */ - public float getWidth() { - return width; - } - - /** - * Sets the color of the polyline. - * - * @param color - the color in ARGB format - */ - public void setColor(int color) { - this.color = color; - update(); - } - - /** - * Sets the width of the polyline. - * - * @param width in pixels - */ - public void setWidth(float width) { - this.width = width; - update(); - } - - @Override - void update() { - MapboxMap mapboxMap = getMapboxMap(); - if (mapboxMap != null) { - mapboxMap.updatePolyline(this); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolylineOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolylineOptions.java deleted file mode 100644 index 6a54f948b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/PolylineOptions.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.os.Parcel; -import android.os.Parcelable; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import java.util.ArrayList; -import java.util.List; - -/** - * Builder for composing {@link Polyline} objects. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ -@Deprecated -public final class PolylineOptions implements Parcelable { - - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public PolylineOptions createFromParcel(@NonNull Parcel in) { - return new PolylineOptions(in); - } - - public PolylineOptions[] newArray(int size) { - return new PolylineOptions[size]; - } - }; - - private PolylineOptions(Parcel in) { - polyline = new Polyline(); - ArrayList pointsList = new ArrayList<>(); - in.readList(pointsList, LatLng.class.getClassLoader()); - addAll(pointsList); - alpha(in.readFloat()); - color(in.readInt()); - width(in.readFloat()); - } - - /** - * Describe the kinds of special objects contained in this Parcelable's - * marshalled representation. - * - * @return integer 0. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. May be 0 or - * {@link #PARCELABLE_WRITE_RETURN_VALUE}. - */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeList(getPoints()); - out.writeFloat(getAlpha()); - out.writeInt(getColor()); - out.writeFloat(getWidth()); - } - - private Polyline polyline; - - /** - * Defines options for a polyline. - */ - public PolylineOptions() { - polyline = new Polyline(); - } - - /** - * Adds a vertex to the end of the polyline being built. - * - * @param point {@link LatLng} point to be added to polyline geometry. - * @return This {@link PolylineOptions} object with the given point on the end. - */ - @NonNull - public PolylineOptions add(LatLng point) { - polyline.addPoint(point); - return this; - } - - /** - * Adds vertices to the end of the polyline being built. - * - * @param points {@link LatLng} points defining the polyline geometry. - * @return This {@link PolylineOptions} object with the given point on the end. - */ - @NonNull - public PolylineOptions add(LatLng... points) { - for (LatLng point : points) { - add(point); - } - return this; - } - - /** - * Adds vertices to the end of the polyline being built. - * - * @param points {@link Iterable} list made up of {@link LatLng} points defining the polyline - * geometry - * @return This {@link PolylineOptions} object with the given points on the end. - */ - @NonNull - public PolylineOptions addAll(Iterable points) { - for (LatLng point : points) { - add(point); - } - return this; - } - - /** - * Set the alpha value of the polyline. - * - * @param alpha float value between 0 (not visible) and 1. - * @return This {@link PolylineOptions} object with the given polyline alpha value. - */ - @NonNull - public PolylineOptions alpha(float alpha) { - polyline.setAlpha(alpha); - return this; - } - - /** - * Gets the alpha set for this {@link PolylineOptions} object. - * - * @return float value between 0 and 1 defining the alpha. - */ - public float getAlpha() { - return polyline.getAlpha(); - } - - /** - * Sets the color of the polyline as a 32-bit ARGB color. The default color is black. - * - * @param color 32-bit ARGB color. - * @return This {@link PolylineOptions} object with a new color set. - */ - @NonNull - public PolylineOptions color(int color) { - polyline.setColor(color); - return this; - } - - /** - * Gets the color set for this {@link PolylineOptions} object. - * - * @return The color of the polyline in ARGB format. - */ - public int getColor() { - return polyline.getColor(); - } - - /** - * Do not use this method. Used internally by the SDK. - * - * @return PolyLine The polyline build by this class. - */ - public Polyline getPolyline() { - return polyline; - } - - /** - * Gets the width set for this {@link PolylineOptions} object. - * - * @return The width of the polyline in screen pixels. - */ - public float getWidth() { - return polyline.getWidth(); - } - - /** - * Sets the width of the polyline in screen pixels. The default is 10. - * - * @param width float value defining width of polyline using unit pixels. - * @return This {@link PolylineOptions} object with a new width set. - */ - @NonNull - public PolylineOptions width(float width) { - polyline.setWidth(width); - return this; - } - - /** - * Gets the points set for this {@link PolylineOptions} object. - * - * @return a {@link List} of {@link LatLng}s specifying the vertices of the polyline. - */ - public List getPoints() { - // the getter gives us a copy, which is the safe thing to do... - return polyline.getPoints(); - } - - /** - * Compares this {@link PolylineOptions} object with another {@link PolylineOptions} and - * determines if their color, alpha, width, and vertices match. - * - * @param o Another {@link PolylineOptions} to compare with this object. - * @return True if color, alpha, width, and vertices match this {@link PolylineOptions} object. - * Else, false. - */ - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - PolylineOptions polyline = (PolylineOptions) o; - - if (Float.compare(polyline.getAlpha(), getAlpha()) != 0) { - return false; - } - if (getColor() != polyline.getColor()) { - return false; - } - if (Float.compare(polyline.getWidth(), getWidth()) != 0) { - return false; - } - return !(getPoints() != null ? !getPoints().equals(polyline.getPoints()) : polyline.getPoints() != null); - } - - /** - * Gives an integer which can be used as the bucket number for storing elements of the set/map. - * This bucket number is the address of the element inside the set/map. There's no guarantee - * that this hash value will be consistent between different Java implementations, or even - * between different execution runs of the same program. - * - * @return integer value you can use for storing element. - */ - @Override - public int hashCode() { - int result = 1; - result = 31 * result + (getAlpha() != +0.0f ? Float.floatToIntBits(getAlpha()) : 0); - result = 31 * result + getColor(); - result = 31 * result + (getWidth() != +0.0f ? Float.floatToIntBits(getWidth()) : 0); - result = 31 * result + (getPoints() != null ? getPoints().hashCode() : 0); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/package-info.java deleted file mode 100644 index 1e2dc542f..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/annotations/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Annotation API classes. - */ -package com.mapbox.mapboxsdk.annotations; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java deleted file mode 100644 index 035614a90..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/Attribution.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.mapbox.mapboxsdk.attribution; - -import android.support.annotation.Nullable; - -import java.util.ArrayList; -import java.util.List; - -public class Attribution { - - private static final String OPENSTREETMAP = "OpenStreetMap"; - private static final String OPENSTREETMAP_ABBR = "OSM"; - static final String TELEMETRY = "Telemetry Settings"; - static final String MAPBOX_URL = "https://www.mapbox.com/about/maps/"; - static final String TELEMETRY_URL = "https://www.mapbox.com/telemetry/"; - static final List IMPROVE_MAP_URLS = new ArrayList<>(); - - static { - // Using a List makes URL backwards compatible - IMPROVE_MAP_URLS.add("https://www.mapbox.com/feedback/"); - IMPROVE_MAP_URLS.add("https://www.mapbox.com/map-feedback/"); - IMPROVE_MAP_URLS.add("https://apps.mapbox.com/feedback/"); - } - - private String title; - private String url; - - Attribution(String title, String url) { - this.title = title; - this.url = url; - } - - public String getTitle() { - return title; - } - - public String getTitleAbbreviated() { - if (title.equals(OPENSTREETMAP)) { - return OPENSTREETMAP_ABBR; - } - return title; - } - - public String getUrl() { - return url; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Attribution that = (Attribution) o; - - if (title != null ? !title.equals(that.title) : that.title != null) { - return false; - } - return url != null ? url.equals(that.url) : that.url == null; - } - - @Override - public int hashCode() { - int result = title != null ? title.hashCode() : 0; - result = 31 * result + (url != null ? url.hashCode() : 0); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java deleted file mode 100644 index 25d5de7ea..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionLayout.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.mapbox.mapboxsdk.attribution; - -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -public class AttributionLayout { - - @Nullable - private Bitmap logo; - @Nullable - private PointF anchorPoint; - private boolean shortText; - - public AttributionLayout(@Nullable Bitmap logo, @Nullable PointF anchorPoint, boolean shortText) { - this.logo = logo; - this.anchorPoint = anchorPoint; - this.shortText = shortText; - } - - @Nullable - public Bitmap getLogo() { - return logo; - } - - @Nullable - public PointF getAnchorPoint() { - return anchorPoint; - } - - public boolean isShortText() { - return shortText; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - AttributionLayout that = (AttributionLayout) o; - - if (logo != null ? !logo.equals(that.logo) : that.logo != null) { - return false; - } - return anchorPoint != null ? anchorPoint.equals(that.anchorPoint) : that.anchorPoint == null; - } - - @Override - public int hashCode() { - int result = logo != null ? logo.hashCode() : 0; - result = 31 * result + (anchorPoint != null ? anchorPoint.hashCode() : 0); - return result; - } - - @NonNull - @Override - public String toString() { - return "AttributionLayout{" - + "logo=" + logo - + ", anchorPoint=" + anchorPoint - + '}'; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java deleted file mode 100644 index b40a7be24..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionMeasure.java +++ /dev/null @@ -1,249 +0,0 @@ -package com.mapbox.mapboxsdk.attribution; - -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.widget.TextView; - -import java.util.Arrays; -import java.util.List; - -public class AttributionMeasure { - - private Bitmap logo; - private Bitmap logoSmall; - private Bitmap snapshot; - private TextView textView; - private TextView textViewShort; - private float margin; - - private boolean shorterText; - - AttributionMeasure(Bitmap snapshot, Bitmap logo, Bitmap logoSmall, TextView tv, TextView tvShort, float margin) { - this.snapshot = snapshot; - this.logo = logo; - this.logoSmall = logoSmall; - this.textView = tv; - this.textViewShort = tvShort; - this.margin = margin; - } - - @Nullable - public AttributionLayout measure() { - Chain chain = new Chain( - new FullLogoLongTextCommand(), - new FullLogoShortTextCommand(), - new SmallLogoLongTextCommand(), - new SmallLogoShortTextCommand(), - new LongTextCommand(), - new ShortTextCommand(), - new NoTextCommand() - ); - - AttributionLayout attributionLayout = chain.start(this); - shorterText = attributionLayout.isShortText(); - return attributionLayout; - } - - - private static class FullLogoLongTextCommand implements Command { - @Nullable - public AttributionLayout execute(@NonNull AttributionMeasure measure) { - float width = measure.getLogoContainerWidth() + measure.getTextViewContainerWidth(); - boolean fitBounds = width <= measure.getMaxSize(); - if (fitBounds) { - PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin); - return new AttributionLayout(measure.logo, anchor, false); - } - return null; - } - } - - private static class FullLogoShortTextCommand implements Command { - @Nullable - @Override - public AttributionLayout execute(@NonNull AttributionMeasure measure) { - float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth(); - boolean fitBounds = width <= measure.getMaxSizeShort(); - if (fitBounds) { - PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin); - return new AttributionLayout(measure.logo, anchor, true); - } - return null; - } - } - - private static class SmallLogoLongTextCommand implements Command { - @Nullable - @Override - public AttributionLayout execute(@NonNull AttributionMeasure measure) { - float width = measure.getLogoSmallContainerWidth() + measure.getTextViewContainerWidth(); - boolean fitBounds = width <= measure.getMaxSize(); - if (fitBounds) { - PointF anchor = calculateAnchor(measure.snapshot, measure.textView, measure.margin); - return new AttributionLayout(measure.logoSmall, anchor, false); - } - return null; - } - } - - private static class SmallLogoShortTextCommand implements Command { - @Nullable - @Override - public AttributionLayout execute(@NonNull AttributionMeasure measure) { - float width = measure.getLogoContainerWidth() + measure.getTextViewShortContainerWidth(); - boolean fitBounds = width <= measure.getMaxSizeShort(); - if (fitBounds) { - PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin); - return new AttributionLayout(measure.logoSmall, anchor, true); - } - return null; - } - } - - private static class LongTextCommand implements Command { - @Nullable - @Override - public AttributionLayout execute(@NonNull AttributionMeasure measure) { - float width = measure.getTextViewContainerWidth() + measure.margin; - boolean fitBounds = width <= measure.getMaxSize(); - if (fitBounds) { - return new AttributionLayout(null, calculateAnchor(measure.snapshot, measure.textView, measure.margin), false); - } - return null; - } - } - - private static class ShortTextCommand implements Command { - @Nullable - @Override - public AttributionLayout execute(@NonNull AttributionMeasure measure) { - float width = measure.getTextViewShortContainerWidth() + measure.margin; - boolean fitBounds = width <= measure.getMaxSizeShort(); - if (fitBounds) { - PointF anchor = calculateAnchor(measure.snapshot, measure.textViewShort, measure.margin); - return new AttributionLayout(null, anchor, true); - } - return null; - } - } - - private static class NoTextCommand implements Command { - @NonNull - @Override - public AttributionLayout execute(AttributionMeasure measure) { - return new AttributionLayout(null, null, false); - } - } - - private static PointF calculateAnchor(Bitmap snapshot, TextView textView, float margin) { - return new PointF( - snapshot.getWidth() - textView.getMeasuredWidth() - margin, - snapshot.getHeight() - margin - textView.getMeasuredHeight() - ); - } - - public TextView getTextView() { - return shorterText ? textViewShort : textView; - } - - private class Chain { - public List commands; - - Chain(Command... commands) { - this.commands = Arrays.asList(commands); - } - - @Nullable - public AttributionLayout start(AttributionMeasure measure) { - AttributionLayout attributionLayout = null; - for (Command command : commands) { - attributionLayout = command.execute(measure); - if (attributionLayout != null) { - break; - } - } - return attributionLayout; - } - } - - public interface Command { - @Nullable - AttributionLayout execute(AttributionMeasure measure); - } - - private float getTextViewContainerWidth() { - return textView.getMeasuredWidth() + margin; - } - - private float getLogoContainerWidth() { - return logo.getWidth() + (2 * margin); - } - - private float getTextViewShortContainerWidth() { - return textViewShort.getMeasuredWidth() + margin; - } - - private float getLogoSmallContainerWidth() { - return logoSmall.getWidth() + (2 * margin); - } - - private float getMaxSize() { - return snapshot.getWidth() * 8 / 10; - } - - private float getMaxSizeShort() { - return snapshot.getWidth(); - } - - public static class Builder { - private Bitmap snapshot; - private Bitmap logo; - private Bitmap logoSmall; - private TextView textView; - private TextView textViewShort; - private float marginPadding; - - @NonNull - public Builder setSnapshot(Bitmap snapshot) { - this.snapshot = snapshot; - return this; - } - - @NonNull - public Builder setLogo(Bitmap logo) { - this.logo = logo; - return this; - } - - @NonNull - public Builder setLogoSmall(Bitmap logoSmall) { - this.logoSmall = logoSmall; - return this; - } - - @NonNull - public Builder setTextView(TextView textView) { - this.textView = textView; - return this; - } - - @NonNull - public Builder setTextViewShort(TextView textViewShort) { - this.textViewShort = textViewShort; - return this; - } - - @NonNull - public Builder setMarginPadding(float marginPadding) { - this.marginPadding = marginPadding; - return this; - } - - @NonNull - public AttributionMeasure build() { - return new AttributionMeasure(snapshot, logo, logoSmall, textView, textViewShort, marginPadding); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java deleted file mode 100644 index ff59dbdf9..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/AttributionParser.java +++ /dev/null @@ -1,314 +0,0 @@ -package com.mapbox.mapboxsdk.attribution; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.text.Html; -import android.text.SpannableStringBuilder; -import android.text.Spanned; -import android.text.style.URLSpan; -import com.mapbox.mapboxsdk.R; - -import java.lang.ref.WeakReference; -import java.util.LinkedHashSet; -import java.util.Set; - -/** - * Responsible for parsing attribution data coming from Sources and MapSnapshot. - *

- * Exposes multiple configuration options to manipulate data being parsed. - * Use the Options object to build these configurations. - *

- */ -public class AttributionParser { - - private static final String IMPROVE_THIS_MAP = "Improve this map"; - private final WeakReference context; - private final Set attributions = new LinkedHashSet<>(); - private final String attributionData; - private final boolean withImproveMap; - private final boolean withCopyrightSign; - private final boolean withTelemetryAttribution; - private final boolean withMapboxAttribution; - - AttributionParser(WeakReference context, String attributionData, boolean withImproveMap, - boolean withCopyrightSign, boolean withTelemetryAttribution, boolean withMapboxAttribution) { - this.context = context; - this.attributionData = attributionData; - this.withImproveMap = withImproveMap; - this.withCopyrightSign = withCopyrightSign; - this.withTelemetryAttribution = withTelemetryAttribution; - this.withMapboxAttribution = withMapboxAttribution; - } - - /** - * Get parsed attributions. - * - * @return the attributions - */ - @NonNull - public Set getAttributions() { - return attributions; - } - - /** - * Get parsed attribution string. - * - * @return the parsed attribution string - */ - @NonNull - public String createAttributionString() { - return createAttributionString(false); - } - - /** - * Get parsed attribution string. - * - * @param shortenedOutput if attribution string should contain shortened output - * @return the parsed attribution string - */ - @NonNull - public String createAttributionString(boolean shortenedOutput) { - StringBuilder stringBuilder = new StringBuilder(withCopyrightSign ? "" : "© "); - int counter = 0; - for (Attribution attribution : attributions) { - counter++; - stringBuilder.append(!shortenedOutput ? attribution.getTitle() : attribution.getTitleAbbreviated()); - if (counter != attributions.size()) { - stringBuilder.append(" / "); - } - } - return stringBuilder.toString(); - } - - /** - * Main attribution for configuration - */ - protected void parse() { - parseAttributions(); - addAdditionalAttributions(); - } - - /** - * Parse attributions - */ - private void parseAttributions() { - SpannableStringBuilder htmlBuilder = (SpannableStringBuilder) fromHtml(attributionData); - URLSpan[] urlSpans = htmlBuilder.getSpans(0, htmlBuilder.length(), URLSpan.class); - for (URLSpan urlSpan : urlSpans) { - parseUrlSpan(htmlBuilder, urlSpan); - } - } - - /** - * Parse an URLSpan containing an attribution. - * - * @param htmlBuilder the html builder - * @param urlSpan the url span to be parsed - */ - private void parseUrlSpan(@NonNull SpannableStringBuilder htmlBuilder, @NonNull URLSpan urlSpan) { - String url = urlSpan.getURL(); - if (isUrlValid(url)) { - String anchor = parseAnchorValue(htmlBuilder, urlSpan); - if (isImproveThisMapAnchor(anchor)) { - anchor = translateImproveThisMapAnchor(anchor); - } - attributions.add(new Attribution(anchor, url)); - } - } - - /** - * Invoked to validate if an url is valid to be included in the final attribution. - * - * @param url the url to be validated - * @return if the url is valid - */ - private boolean isUrlValid(@NonNull String url) { - return isValidForImproveThisMap(url) && isValidForMapbox(url); - } - - /** - * Invoked to validate if an anchor is equal to Improve this map coming from tilesets. - * - * @param anchor the anchor to be validated - * @return if the url is valid - */ - private boolean isImproveThisMapAnchor(String anchor) { - return anchor.equals(IMPROVE_THIS_MAP); - } - - /** - * Invoked to replace the english Improve this map with localized variant. - * - * @param anchor the anchor to be translated - * @return the translated anchor - */ - private String translateImproveThisMapAnchor(String anchor) { - Context context = this.context.get(); - if (context != null) { - anchor = context.getString(R.string.mapbox_telemetryImproveMap); - } - return anchor; - } - - /** - * Invoked to validate if an url is valid for the improve map configuration. - * - * @param url the url to be validated - * @return if the url is valid for improve this map - */ - private boolean isValidForImproveThisMap(@NonNull String url) { - return withImproveMap || !(Attribution.IMPROVE_MAP_URLS.contains(url)); - } - - /** - * Invoked to validate if an url is valid for the Mapbox configuration. - * - * @param url the url to be validated - * @return if the url is valid for Mapbox - */ - private boolean isValidForMapbox(@NonNull String url) { - return withMapboxAttribution || !url.equals(Attribution.MAPBOX_URL); - } - - /** - * Parse the attribution by parsing the anchor value of html href tag. - * - * @param htmlBuilder the html builder - * @param urlSpan the current urlSpan - * @return the parsed anchor value - */ - @NonNull - private String parseAnchorValue(SpannableStringBuilder htmlBuilder, URLSpan urlSpan) { - int start = htmlBuilder.getSpanStart(urlSpan); - int end = htmlBuilder.getSpanEnd(urlSpan); - int length = end - start; - char[] charKey = new char[length]; - htmlBuilder.getChars(start, end, charKey, 0); - return stripCopyright(String.valueOf(charKey)); - } - - /** - * Utility to strip the copyright sign from an attribution - * - * @param anchor the attribution string to strip - * @return the stripped attribution string without the copyright sign - */ - @NonNull - private String stripCopyright(@NonNull String anchor) { - if (!withCopyrightSign && anchor.startsWith("© ")) { - anchor = anchor.substring(2, anchor.length()); - } - return anchor; - } - - /** - * Invoked to manually add attributions - */ - private void addAdditionalAttributions() { - if (withTelemetryAttribution) { - Context context = this.context.get(); - attributions.add( - new Attribution( - context != null ? context.getString(R.string.mapbox_telemetrySettings) : Attribution.TELEMETRY, - Attribution.TELEMETRY_URL - ) - ); - } - } - - /** - * Convert a string to a spanned html representation. - * - * @param html the string to convert - * @return the spanned html representation - */ - private static Spanned fromHtml(String html) { - Spanned result; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { - result = Html.fromHtml(html, Html.FROM_HTML_MODE_LEGACY); - } else { - result = Html.fromHtml(html); - } - return result; - } - - /** - * Builder to configure using an AttributionParser. - *

- * AttributionData, set with {@link #withAttributionData(String...)}, is the only required property to build - * the underlying AttributionParser. Other properties include trimming the copyright sign, adding telemetry - * attribution or hiding attribution as improve this map and Mapbox. - *

- */ - public static class Options { - private final WeakReference context; - private boolean withImproveMap = true; - private boolean withCopyrightSign = true; - private boolean withTelemetryAttribution = false; - private boolean withMapboxAttribution = true; - private String[] attributionDataStringArray; - - public Options(@NonNull Context context) { - this.context = new WeakReference<>(context); - } - - @NonNull - public Options withAttributionData(String... attributionData) { - this.attributionDataStringArray = attributionData; - return this; - } - - @NonNull - public Options withImproveMap(boolean withImproveMap) { - this.withImproveMap = withImproveMap; - return this; - } - - @NonNull - public Options withCopyrightSign(boolean withCopyrightSign) { - this.withCopyrightSign = withCopyrightSign; - return this; - } - - @NonNull - public Options withTelemetryAttribution(boolean withTelemetryAttribution) { - this.withTelemetryAttribution = withTelemetryAttribution; - return this; - } - - @NonNull - public Options withMapboxAttribution(boolean withMapboxAttribution) { - this.withMapboxAttribution = withMapboxAttribution; - return this; - } - - @NonNull - public AttributionParser build() { - if (attributionDataStringArray == null) { - throw new IllegalStateException("Using builder without providing attribution data"); - } - - String fullAttributionString = parseAttribution(attributionDataStringArray); - AttributionParser attributionParser = new AttributionParser( - context, - fullAttributionString, - withImproveMap, - withCopyrightSign, - withTelemetryAttribution, - withMapboxAttribution - ); - attributionParser.parse(); - return attributionParser; - } - - private String parseAttribution(String[] attribution) { - StringBuilder builder = new StringBuilder(); - for (String attr : attribution) { - if (!attr.isEmpty()) { - builder.append(attr); - } - } - return builder.toString(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java deleted file mode 100644 index 57d840af5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/attribution/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Attribution API classes. - */ -package com.mapbox.mapboxsdk.attribution; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java deleted file mode 100644 index fb89688e2..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraPosition.java +++ /dev/null @@ -1,414 +0,0 @@ -package com.mapbox.mapboxsdk.camera; - -import android.content.res.TypedArray; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.FloatRange; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.Size; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.utils.MathUtils; - -import java.util.Arrays; - -/** - * Resembles the position, angle, zoom and tilt of the user's viewpoint. - */ -public final class CameraPosition implements Parcelable { - - public static final CameraPosition DEFAULT = new CameraPosition(new LatLng(), 0, 0, 0, new double[] {0, 0, 0, 0}); - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public CameraPosition createFromParcel(Parcel in) { - double bearing = in.readDouble(); - LatLng target = in.readParcelable(LatLng.class.getClassLoader()); - double tilt = in.readDouble(); - double zoom = in.readDouble(); - - double[] padding = null; - int paddingSize = in.readInt(); - if (paddingSize > 0) { - padding = new double[paddingSize]; - for (int i = 0; i < paddingSize; i++) { - padding[i] = in.readDouble(); - } - } - - return new CameraPosition(target, zoom, tilt, bearing, padding); - } - - public CameraPosition[] newArray(int size) { - return new CameraPosition[size]; - } - }; - - /** - * Direction that the camera is pointing in, in degrees clockwise from north. - */ - @Keep - public final double bearing; - - /** - * The location that the camera is pointing at. - */ - @Keep - public final LatLng target; - - /** - * The angle, in degrees, of the camera angle from the nadir (directly facing the Earth). - * See {@link Builder#tilt(double)} for details of restrictions on the range of values. - */ - @Keep - public final double tilt; - - /** - * Zoom level near the center of the screen. See {@link Builder#zoom(double)} for the definition of the camera's - * zoom level. - */ - @Keep - public final double zoom; - - /** - * Padding in pixels. Specified in left, top, right, bottom order. - * See {@link Builder#padding(double[])} for the definition of the camera's padding. - */ - @Keep - public final double[] padding; - - /** - * Constructs a CameraPosition. - * - * @param target The target location to align with the center of the screen. - * @param zoom Zoom level at target. See zoom(float) for details of restrictions. - * @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float) - * for details of restrictions. - * @param bearing Direction that the camera is pointing in, in degrees clockwise from north. - * This value will be normalized to be within 0 degrees inclusive and 360 degrees - * exclusive. - * @throws NullPointerException if target is null - * @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive. - * @deprecated use {@link CameraPosition#CameraPosition(LatLng, double, double, double, double[])} instead. - */ - @Deprecated - CameraPosition(LatLng target, double zoom, double tilt, double bearing) { - this(target, zoom, tilt, bearing, null); - } - - /** - * Constructs a CameraPosition. - * - * @param target The target location to align with the center of the screen. - * @param zoom Zoom level at target. See zoom(float) for details of restrictions. - * @param tilt The camera angle, in degrees, from the nadir (directly down). See tilt(float) - * for details of restrictions. - * @param bearing Direction that the camera is pointing in, in degrees clockwise from north. - * This value will be normalized to be within 0 degrees inclusive and 360 degrees - * exclusive. - * @param padding Padding in pixels. Specified in left, top, right, bottom order. - * @throws NullPointerException if target is null - * @throws IllegalArgumentException if tilt is outside the range of 0 to 90 degrees inclusive. - */ - @Keep - CameraPosition(LatLng target, double zoom, double tilt, double bearing, double[] padding) { - this.target = target; - this.bearing = bearing; - this.tilt = tilt; - this.zoom = zoom; - this.padding = padding; - } - - /** - * Describe the kinds of special objects contained in this Parcelable's - * marshalled representation. - * - * @return integer 0. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written. May be 0 or - * {@link #PARCELABLE_WRITE_RETURN_VALUE}. - */ - @Override - public void writeToParcel(Parcel out, int flags) { - out.writeDouble(bearing); - out.writeParcelable(target, flags); - out.writeDouble(tilt); - out.writeDouble(zoom); - - if (padding != null) { - int length = padding.length; - out.writeInt(length); - for (double v : padding) { - out.writeDouble(v); - } - } else { - out.writeInt(-1); - } - } - - /** - * Returns a String with the camera target, zoom, bearing and tilt. - * - * @return A String with CameraPosition information. - */ - @Override - public String toString() { - return "Target: " + target + ", Zoom:" + zoom + ", Bearing:" + bearing + ", Tilt:" + tilt - + ", Padding:" + Arrays.toString(padding); - } - - /** - * Compares this {@link CameraPosition} object with another {@link CameraPosition} and - * determines if their target, zoom, tilt, and bearing match. - * - * @param o Another {@link CameraPosition} to compare with this object. - * @return True if target, zoom, tilt, and bearing match this {@link CameraPosition} object. - * Else, false. - */ - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - - if (o == null || getClass() != o.getClass()) { - return false; - } - - CameraPosition cameraPosition = (CameraPosition) o; - if (target != null && !target.equals(cameraPosition.target)) { - return false; - } else if (zoom != cameraPosition.zoom) { - return false; - } else if (tilt != cameraPosition.tilt) { - return false; - } else if (bearing != cameraPosition.bearing) { - return false; - } else if (!Arrays.equals(padding, cameraPosition.padding)) { - return false; - } - return true; - } - - /** - * Gives an integer which can be used as the bucket number for storing elements of the set/map. - * This bucket number is the address of the element inside the set/map. There's no guarantee - * that this hash value will be consistent between different Java implementations, or even - * between different execution runs of the same program. - * - * @return integer value you can use for storing element. - */ - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(bearing); - result = (int) (temp ^ (temp >>> 32)); - result = 31 * result + (target != null ? target.hashCode() : 0); - temp = Double.doubleToLongBits(tilt); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(zoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + Arrays.hashCode(padding); - return result; - } - - /** - * Builder for composing CameraPosition objects. - */ - public static final class Builder { - - private double bearing = -1; - @Nullable - private LatLng target = null; - private double tilt = -1; - private double zoom = -1; - private double[] padding = null; - - /** - * Create an empty builder. - */ - public Builder() { - super(); - } - - /** - * Create a builder with an existing CameraPosition data. - * - * @param previous Existing CameraPosition values to use - */ - public Builder(@Nullable CameraPosition previous) { - super(); - if (previous != null) { - this.bearing = previous.bearing; - this.target = previous.target; - this.tilt = previous.tilt; - this.zoom = previous.zoom; - this.padding = previous.padding; - } - } - - /** - * Create a builder with an existing CameraPosition data. - * - * @param typedArray TypedArray containing attribute values - */ - public Builder(@Nullable TypedArray typedArray) { - super(); - if (typedArray != null) { - this.bearing = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraBearing, 0.0f); - double lat = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTargetLat, 0.0f); - double lng = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTargetLng, 0.0f); - this.target = new LatLng(lat, lng); - this.tilt = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTilt, 0.0f); - this.zoom = typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoom, 0.0f); - } - } - - /** - * Create a builder from an existing CameraPositionUpdate update. - * - * @param update Update containing camera options - */ - public Builder(@Nullable CameraUpdateFactory.CameraPositionUpdate update) { - super(); - if (update != null) { - bearing = update.getBearing(); - target = update.getTarget(); - tilt = update.getTilt(); - zoom = update.getZoom(); - padding = update.getPadding(); - } - } - - /** - * Create builder from an existing CameraPositionUpdate update. - * - * @param update Update containing camera options - */ - public Builder(@Nullable CameraUpdateFactory.ZoomUpdate update) { - super(); - if (update != null) { - this.zoom = update.getZoom(); - } - } - - /** - * Sets the direction that the camera is pointing in, in degrees clockwise from north. - * - * @param bearing Bearing - * @return this - */ - @NonNull - public Builder bearing(double bearing) { - double direction = bearing; - - while (direction >= 360) { - direction -= 360; - } - while (direction < 0) { - direction += 360; - } - - this.bearing = direction; - return this; - } - - /** - * Sets the location where the camera is pointing at. - * - * @param location target of the camera - * @return this - */ - @NonNull - public Builder target(LatLng location) { - this.target = location; - return this; - } - - /** - * Set the tilt of the camera in degrees - *

- * value is clamped to {@link MapboxConstants#MINIMUM_TILT} and {@link MapboxConstants#MAXIMUM_TILT}. - *

- * - * @param tilt Tilt value of the camera - * @return this - */ - @NonNull - public Builder tilt(@FloatRange(from = MapboxConstants.MINIMUM_TILT, - to = MapboxConstants.MAXIMUM_TILT) double tilt) { - this.tilt = MathUtils.clamp(tilt, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT); - return this; - } - - /** - * Set the zoom of the camera - *

- * Zoom ranges from {@link MapboxConstants#MINIMUM_ZOOM} to {@link MapboxConstants#MAXIMUM_ZOOM} - *

- * - * @param zoom Zoom value of the camera - * @return this - */ - @NonNull - public Builder zoom(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, - to = MapboxConstants.MAXIMUM_ZOOM) double zoom) { - this.zoom = zoom; - return this; - } - - /** - * Padding in pixels that shifts the viewport by the specified amount. - * Applied padding is going to persist and impact following camera transformations. - *

- * Specified in left, top, right, bottom order. - *

- * - * @param padding Camera padding - * @return this - */ - @NonNull - public Builder padding(@Size(4) double[] padding) { - this.padding = padding; - return this; - } - - /** - * Padding in pixels that shifts the viewport by the specified amount. - * Applied padding is going to persist and impact following camera transformations. - *

- * Specified in left, top, right, bottom order. - *

- * - * @return this - */ - @NonNull - public Builder padding(double left, double top, double right, double bottom) { - this.padding = new double[] {left, top, right, bottom}; - return this; - } - - /** - * Builds the CameraPosition. - * - * @return CameraPosition - */ - public CameraPosition build() { - return new CameraPosition(target, zoom, tilt, bearing, padding); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java deleted file mode 100644 index 39b937210..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdate.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.mapbox.mapboxsdk.camera; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -/** - * Interface definition for camera updates. - */ -public interface CameraUpdate { - - /** - * Get the camera position from the camera update. - * - * @param mapboxMap Map object to build the position from - * @return the camera position from the implementing camera update - */ - @Nullable - CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap); - -} - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java deleted file mode 100644 index b611456f4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/CameraUpdateFactory.java +++ /dev/null @@ -1,637 +0,0 @@ -package com.mapbox.mapboxsdk.camera; - -import android.graphics.Point; -import android.graphics.PointF; -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.mapboxsdk.maps.UiSettings; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; - -/** - * Factory for creating CameraUpdate objects. - */ -public final class CameraUpdateFactory { - - /** - * Returns a CameraUpdate that moves the camera to a specified CameraPosition. - * - * @param cameraPosition Camera Position to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newCameraPosition(@NonNull CameraPosition cameraPosition) { - return new CameraPositionUpdate(cameraPosition.bearing, cameraPosition.target, cameraPosition.tilt, - cameraPosition.zoom, cameraPosition.padding); - } - - /** - * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude - * specified by a LatLng object. This centers the camera on the LatLng object. - * - * @param latLng Target location to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLng(@NonNull LatLng latLng) { - return new CameraPositionUpdate(-1, latLng, -1, -1, null); - } - - /** - * Returns a CameraUpdate that transforms the camera such that the specified - * latitude/longitude bounds are centered on screen at the greatest possible zoom level while maintaining - * current camera position bearing and tilt values. - *

- * You can specify padding, in order to inset the bounding box from the map view's edges. - * The padding will not persist and impact following camera transformations. - *

- * - * @param bounds Bounds to match Camera position with - * @param padding Padding added to the bounds - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, int padding) { - return newLatLngBounds(bounds, padding, padding, padding, padding); - } - - /** - * Returns a CameraUpdate that transforms the camera such that the specified - * latitude/longitude bounds are centered on screen at the greatest possible zoom level while using - * provided bearing and tilt values. - *

- * You can specify padding, in order to inset the bounding box from the map view's edges. - * The padding will not persist and impact following camera transformations. - *

- * - * @param bounds Bounds to match Camera position with - * @param bearing Bearing to take in account when generating the bounds - * @param tilt Tilt to take in account when generating the bounds - * @param padding Padding added to the bounds - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, double bearing, double tilt, int padding) { - return newLatLngBounds(bounds, bearing, tilt, padding, padding, padding, padding); - } - - /** - * Returns a CameraUpdate that transforms the camera such that the specified - * latitude/longitude bounds are centered on screen at the greatest possible zoom level while maintaining - * current camera position bearing and tilt values. - *

- * You can specify padding, in order to inset the bounding box from the map view's edges. - * The padding will not persist and impact following camera transformations. - *

- * - * @param bounds Bounds to base the Camera position out of - * @param paddingLeft Padding left of the bounds - * @param paddingTop Padding top of the bounds - * @param paddingRight Padding right of the bounds - * @param paddingBottom Padding bottom of the bounds - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, int paddingLeft, int paddingTop, - int paddingRight, int paddingBottom) { - return new CameraBoundsUpdate(bounds, null, null, paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - /** - * Returns a CameraUpdate that transforms the camera such that the specified - * latitude/longitude bounds are centered on screen at the greatest possible zoom level while using - * provided bearing and tilt values. - *

- * You can specify padding, in order to inset the bounding box from the map view's edges. - * The padding will not persist and impact following camera transformations. - *

- * - * @param bounds Bounds to base the Camera position out of - * @param bearing Bearing to take in account when generating the bounds - * @param tilt Tilt to take in account when generating the bounds - * @param paddingLeft Padding left of the bounds - * @param paddingTop Padding top of the bounds - * @param paddingRight Padding right of the bounds - * @param paddingBottom Padding bottom of the bounds - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLngBounds(@NonNull LatLngBounds bounds, double bearing, double tilt, - int paddingLeft, int paddingTop, int paddingRight, int paddingBottom) { - return new CameraBoundsUpdate(bounds, bearing, tilt, paddingLeft, paddingTop, paddingRight, paddingBottom); - } - - /** - * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude - * specified by a LatLng object taking the specified padding into account. - * - * @param latLng Target location to change to - * @param zoom Zoom level to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLngZoom(@NonNull LatLng latLng, double zoom) { - return new CameraPositionUpdate(-1, latLng, -1, zoom, null); - } - - /** - * Returns a CameraUpdate that moves the center of the screen to a latitude and longitude - * specified by a LatLng object, and moves to the given zoom level. - * Applied padding is going to persist and impact following camera transformations. - * - * @param latLng Target location to change to - * @param left Left padding - * @param top Top padding - * @param right Right padding - * @param bottom Bottom padding - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate newLatLngPadding(@NonNull LatLng latLng, - double left, double top, double right, double bottom) { - return new CameraPositionUpdate(-1, latLng, -1, -1, new double[] {left, top, right, bottom}); - } - - /** - * Returns a CameraUpdate that shifts the zoom level of the current camera viewpoint. - * - * @param amount Amount of zoom level to change with - * @param focus Focus point of zoom - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate zoomBy(double amount, Point focus) { - return new ZoomUpdate(amount, focus.x, focus.y); - } - - /** - * Returns a CameraUpdate that shifts the zoom level of the current camera viewpoint. - * - * @param amount Amount of zoom level to change with - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate zoomBy(double amount) { - return new ZoomUpdate(ZoomUpdate.ZOOM_BY, amount); - } - - /** - * Returns a CameraUpdate that zooms in on the map by moving the viewpoint's height closer to - * the Earth's surface. The zoom increment is 1.0. - * - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate zoomIn() { - return new ZoomUpdate(ZoomUpdate.ZOOM_IN); - } - - /** - * Returns a CameraUpdate that zooms out on the map by moving the viewpoint's height farther - * away from the Earth's surface. The zoom increment is -1.0. - * - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate zoomOut() { - return new ZoomUpdate(ZoomUpdate.ZOOM_OUT); - } - - /** - * Returns a CameraUpdate that moves the camera viewpoint to a particular zoom level. - * - * @param zoom Zoom level to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate zoomTo(double zoom) { - return new ZoomUpdate(ZoomUpdate.ZOOM_TO, zoom); - } - - /** - * Returns a CameraUpdate that moves the camera viewpoint to a particular bearing. - * - * @param bearing Bearing to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate bearingTo(double bearing) { - return new CameraPositionUpdate(bearing, null, -1, -1, null); - } - - /** - * Returns a CameraUpdate that moves the camera viewpoint to a particular tilt. - * - * @param tilt Tilt to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate tiltTo(double tilt) { - return new CameraPositionUpdate(-1, null, tilt, -1, null); - } - - /** - * Returns a CameraUpdate that when animated changes the camera padding. - * Applied padding is going to persist and impact following camera transformations. - *

- * Specified in left, top, right, bottom order. - *

- * - * @param padding Padding to change to - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate paddingTo(double[] padding) { - return new CameraPositionUpdate(-1, null, -1, -1, padding); - } - - /** - * Returns a CameraUpdate that when animated changes the camera padding. - * Applied padding is going to persist and impact following camera transformations. - *

- * Specified in left, top, right, bottom order. - *

- * - * @return CameraUpdate Final Camera Position - */ - public static CameraUpdate paddingTo(double left, double top, double right, double bottom) { - return paddingTo(new double[] {left, top, right, bottom}); - } - - // - // CameraUpdate types - // - - static final class CameraPositionUpdate implements CameraUpdate { - - private final double bearing; - private final LatLng target; - private final double tilt; - private final double zoom; - private final double[] padding; - - CameraPositionUpdate(double bearing, LatLng target, double tilt, double zoom, double[] padding) { - this.bearing = bearing; - this.target = target; - this.tilt = tilt; - this.zoom = zoom; - this.padding = padding; - } - - public LatLng getTarget() { - return target; - } - - public double getBearing() { - return bearing; - } - - public double getTilt() { - return tilt; - } - - public double getZoom() { - return zoom; - } - - public double[] getPadding() { - return padding; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - if (target == null) { - CameraPosition previousPosition = mapboxMap.getCameraPosition(); - return new CameraPosition.Builder(this) - .target(previousPosition.target) - .build(); - } - return new CameraPosition.Builder(this).build(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CameraPositionUpdate that = (CameraPositionUpdate) o; - - if (Double.compare(that.bearing, bearing) != 0) { - return false; - } - if (Double.compare(that.tilt, tilt) != 0) { - return false; - } - if (Double.compare(that.zoom, zoom) != 0) { - return false; - } - if (target != null ? !target.equals(that.target) : that.target != null) { - return false; - } - return Arrays.equals(padding, that.padding); - } - - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(bearing); - result = (int) (temp ^ (temp >>> 32)); - result = 31 * result + (target != null ? target.hashCode() : 0); - temp = Double.doubleToLongBits(tilt); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(zoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + Arrays.hashCode(padding); - return result; - } - - @Override - public String toString() { - return "CameraPositionUpdate{" - + "bearing=" + bearing - + ", target=" + target - + ", tilt=" + tilt - + ", zoom=" + zoom - + ", padding=" + Arrays.toString(padding) - + '}'; - } - } - - static final class CameraBoundsUpdate implements CameraUpdate { - - private final LatLngBounds bounds; - private final int[] padding; - private final Double bearing; - private final Double tilt; - - CameraBoundsUpdate(LatLngBounds bounds, Double bearing, Double tilt, int[] padding) { - this.bounds = bounds; - this.padding = padding; - this.bearing = bearing; - this.tilt = tilt; - } - - CameraBoundsUpdate(LatLngBounds bounds, Double bearing, Double tilt, int paddingLeft, - int paddingTop, int paddingRight, int paddingBottom) { - this(bounds, bearing, tilt, new int[] {paddingLeft, paddingTop, paddingRight, paddingBottom}); - } - - public LatLngBounds getBounds() { - return bounds; - } - - public int[] getPadding() { - return padding; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - if (bearing == null && tilt == null) { - // use current camera position tilt and bearing - return mapboxMap.getCameraForLatLngBounds(bounds, padding); - } else { - // use provided tilt and bearing - assert bearing != null; - assert tilt != null; - return mapboxMap.getCameraForLatLngBounds(bounds, padding, bearing, tilt); - } - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CameraBoundsUpdate that = (CameraBoundsUpdate) o; - - if (!bounds.equals(that.bounds)) { - return false; - } - return Arrays.equals(padding, that.padding); - } - - @Override - public int hashCode() { - int result = bounds.hashCode(); - result = 31 * result + Arrays.hashCode(padding); - return result; - } - - @Override - public String toString() { - return "CameraBoundsUpdate{" - + "bounds=" + bounds - + ", padding=" + Arrays.toString(padding) - + '}'; - } - } - - static final class CameraMoveUpdate implements CameraUpdate { - - private float x; - private float y; - - CameraMoveUpdate(float x, float y) { - this.x = x; - this.y = y; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - UiSettings uiSettings = mapboxMap.getUiSettings(); - Projection projection = mapboxMap.getProjection(); - // Calculate the new center point - float viewPortWidth = uiSettings.getWidth(); - float viewPortHeight = uiSettings.getHeight(); - int[] padding = mapboxMap.getPadding(); - - // we inverse the map padding, is reapplied when using moveTo/easeTo or animateTo - PointF targetPoint = new PointF( - (viewPortWidth - padding[0] + padding[1]) / 2 + x, - (viewPortHeight + padding[1] - padding[3]) / 2 + y - ); - - LatLng latLng = projection.fromScreenLocation(targetPoint); - CameraPosition previousPosition = mapboxMap.getCameraPosition(); - CameraPosition position = - new CameraPosition.Builder() - .target(latLng) - .zoom(previousPosition.zoom) - .tilt(previousPosition.tilt) - .bearing(previousPosition.bearing) - .build(); - return position; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - CameraMoveUpdate that = (CameraMoveUpdate) o; - - if (Float.compare(that.x, x) != 0) { - return false; - } - return Float.compare(that.y, y) == 0; - } - - @Override - public int hashCode() { - int result = (x != +0.0f ? Float.floatToIntBits(x) : 0); - result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0); - return result; - } - - @Override - public String toString() { - return "CameraMoveUpdate{" - + "x=" + x - + ", y=" + y - + '}'; - } - } - - static final class ZoomUpdate implements CameraUpdate { - - @IntDef( {ZOOM_IN, ZOOM_OUT, ZOOM_BY, ZOOM_TO, ZOOM_TO_POINT}) - @Retention(RetentionPolicy.SOURCE) - @interface Type { - } - - static final int ZOOM_IN = 0; - static final int ZOOM_OUT = 1; - static final int ZOOM_BY = 2; - static final int ZOOM_TO = 3; - static final int ZOOM_TO_POINT = 4; - - @Type - private final int type; - private final double zoom; - private float x; - private float y; - - ZoomUpdate(@Type int type) { - this.type = type; - this.zoom = 0; - } - - ZoomUpdate(@Type int type, double zoom) { - this.type = type; - this.zoom = zoom; - } - - ZoomUpdate(double zoom, float x, float y) { - this.type = ZOOM_TO_POINT; - this.zoom = zoom; - this.x = x; - this.y = y; - } - - public double getZoom() { - return zoom; - } - - @Type - public int getType() { - return type; - } - - public float getX() { - return x; - } - - public float getY() { - return y; - } - - double transformZoom(double currentZoom) { - switch (getType()) { - case CameraUpdateFactory.ZoomUpdate.ZOOM_IN: - currentZoom++; - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_OUT: - currentZoom--; - if (currentZoom < 0) { - currentZoom = 0; - } - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_TO: - currentZoom = getZoom(); - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_BY: - currentZoom = currentZoom + getZoom(); - break; - case CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT: - currentZoom = currentZoom + getZoom(); - break; - } - return currentZoom; - } - - @Override - public CameraPosition getCameraPosition(@NonNull MapboxMap mapboxMap) { - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - if (getType() != CameraUpdateFactory.ZoomUpdate.ZOOM_TO_POINT) { - return new CameraPosition.Builder(cameraPosition) - .zoom(transformZoom(cameraPosition.zoom)) - .build(); - } else { - return new CameraPosition.Builder(cameraPosition) - .zoom(transformZoom(cameraPosition.zoom)) - .target(mapboxMap.getProjection().fromScreenLocation(new PointF(getX(), getY()))) - .build(); - } - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - ZoomUpdate that = (ZoomUpdate) o; - - if (type != that.type) { - return false; - } - if (Double.compare(that.zoom, zoom) != 0) { - return false; - } - if (Float.compare(that.x, x) != 0) { - return false; - } - return Float.compare(that.y, y) == 0; - } - - @Override - public int hashCode() { - int result; - long temp; - result = type; - temp = Double.doubleToLongBits(zoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (x != +0.0f ? Float.floatToIntBits(x) : 0); - result = 31 * result + (y != +0.0f ? Float.floatToIntBits(y) : 0); - return result; - } - - @Override - public String toString() { - return "ZoomUpdate{" - + "type=" + type - + ", zoom=" + zoom - + ", x=" + x - + ", y=" + y - + '}'; - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/package-info.java deleted file mode 100644 index 04ef699c2..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/camera/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Camera API classes. - */ -package com.mapbox.mapboxsdk.camera; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java deleted file mode 100644 index 6f6e77792..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/GeometryConstants.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.mapbox.mapboxsdk.constants; - -/** - * Contains constants used throughout the sdk classes. - */ -public class GeometryConstants { - - /** - * The equatorial radius - * value in meters - */ - public static final int RADIUS_EARTH_METERS = 6378137; - - /** - * This constant represents the lowest longitude value available to represent a wrapped geolocation. - */ - public static final double MIN_WRAP_LONGITUDE = -180; - - /** - * This constant represents the highest longitude value available to represent a wrapped geolocation. - */ - public static final double MAX_WRAP_LONGITUDE = 180; - - /** - * This constant represents the lowest longitude value available to represent a geolocation. - */ - public static final double MIN_LONGITUDE = -Double.MAX_VALUE; - - /** - * This constant represents the highest longitude value available to represent a geolocation. - */ - public static final double MAX_LONGITUDE = Double.MAX_VALUE; - - /** - * This constant represents the lowest latitude value available to represent a geolocation. - */ - public static final double MIN_LATITUDE = -90; - - /** - * This constant represents the latitude span when representing a geolocation. - */ - public static final double LATITUDE_SPAN = 180; - - /** - * This constant represents the longitude span when representing a geolocation. - */ - public static final double LONGITUDE_SPAN = 360; - - /** - * This constant represents the highest latitude value available to represent a geolocation. - */ - public static final double MAX_LATITUDE = 90; - - /** - * Maximum latitude value in Mercator projection. - */ - public static final double MAX_MERCATOR_LATITUDE = 85.05112877980659; - - /** - * Minimum latitude value in Mercator projection. - */ - public static final double MIN_MERCATOR_LATITUDE = -85.05112877980659; - - private GeometryConstants() { - // Private constructor to prevent initializing of this class. - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java deleted file mode 100644 index 6156b3af7..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/MapboxConstants.java +++ /dev/null @@ -1,232 +0,0 @@ -package com.mapbox.mapboxsdk.constants; - -import java.util.Locale; - -/** - * MapboxConstants exposes Mapbox related constants - */ -public class MapboxConstants { - - /** - * Default Locale for data processing (ex: String.toLowerCase(MAPBOX_LOCALE, "foo")) - */ - public static final Locale MAPBOX_LOCALE = Locale.US; - - /** - * The name of the desired preferences file for Android's SharedPreferences. - */ - public static final String MAPBOX_SHARED_PREFERENCES = "MapboxSharedPreferences"; - - /** - * Key used to switch storage to external in AndroidManifest.xml - */ - public static final String KEY_META_DATA_SET_STORAGE_EXTERNAL = "com.mapbox.SetStorageExternal"; - - /** - * Default value for KEY_META_DATA_SET_STORAGE_EXTERNAL (default is internal storage) - */ - public static final boolean DEFAULT_SET_STORAGE_EXTERNAL = false; - - /** - * Key used to switch Tile Download Measuring on/off in AndroidManifest.xml - */ - public static final String KEY_META_DATA_MEASURE_TILE_DOWNLOAD_ON = "com.mapbox.MeasureTileDownloadOn"; - - /** - * Default value for KEY_META_DATA_MEASURE_TILE_DOWNLOAD_ON (default is off) - */ - public static final boolean DEFAULT_MEASURE_TILE_DOWNLOAD_ON = false; - - public static final String KEY_PREFERENCE_SKU_TOKEN = "com.mapbox.mapboxsdk.accounts.skutoken"; - - public static final String KEY_META_DATA_MANAGE_SKU_TOKEN = "com.mapbox.ManageSkuToken"; - - public static final boolean DEFAULT_MANAGE_SKU_TOKEN = true; - - /** - * Default value for font fallback for local ideograph fonts - */ - public static final String DEFAULT_FONT = "sans-serif"; - - /** - * Unmeasured state - */ - public static final float UNMEASURED = -1f; - - /** - * Default animation time - */ - public static final int ANIMATION_DURATION = 300; - - /** - * Default short animation time - */ - public static final int ANIMATION_DURATION_SHORT = 150; - - /** - * Animation time of a fling gesture - */ - public static final long ANIMATION_DURATION_FLING_BASE = ANIMATION_DURATION_SHORT; - - /** - * Velocity threshold for a fling gesture - */ - public static final long VELOCITY_THRESHOLD_IGNORE_FLING = 1000; - - /** - * Value by which the default rotation threshold will be increased when scaling - * - * @deprecated unused, see {@link com.mapbox.mapboxsdk.maps.UiSettings#setDisableRotateWhenScaling(boolean)} - */ - @Deprecated - public static final float ROTATION_THRESHOLD_INCREASE_WHEN_SCALING = 25f; - - /** - * Maximum absolute zoom change for multi-pointer scale velocity animation - */ - public static final double MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE = 2.5; - - /** - * Maximum possible zoom change during the quick zoom gesture executed across the whole screen - */ - public static final double QUICK_ZOOM_MAX_ZOOM_CHANGE = 4.0; - - /** - * Scale velocity animation duration multiplier. - */ - public static final double SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER = 150; - - /** - * Minimum angular velocity for rotation animation - * - * @deprecated unused, see {@link #ROTATE_VELOCITY_RATIO_THRESHOLD} - */ - @Deprecated - public static final float MINIMUM_ANGULAR_VELOCITY = 1.5f; - - /** - * Last scale span delta to XY velocity ratio required to execute scale velocity animation. - */ - public static final double SCALE_VELOCITY_RATIO_THRESHOLD = 4 * 1e-3; - - /** - * Last rotation delta to XY velocity ratio required to execute rotation velocity animation. - */ - public static final double ROTATE_VELOCITY_RATIO_THRESHOLD = 2.2 * 1e-4; - - /** - * Time within which user needs to lift fingers for velocity animation to start. - */ - public static final long SCHEDULED_ANIMATION_TIMEOUT = 150L; - - /** - * Maximum angular velocity for rotation animation - */ - public static final float MAXIMUM_ANGULAR_VELOCITY = 30f; - - /** - * Factor to calculate tilt change based on pixel change during shove gesture. - */ - public static final float SHOVE_PIXEL_CHANGE_FACTOR = 0.1f; - - /** - * The currently supported minimum zoom level. - */ - public static final float MINIMUM_ZOOM = 0.0f; - - /** - * The currently supported maximum zoom level. - */ - public static final float MAXIMUM_ZOOM = 25.5f; - - /** - * The currently supported maximum tilt value. - */ - public static final double MAXIMUM_TILT = 60; - - /** - * The currently supported minimum tilt value. - */ - public static final double MINIMUM_TILT = 0; - - /** - * The currently supported maximum direction - */ - public static final double MAXIMUM_DIRECTION = 360; - - /** - * The currently supported minimum direction - */ - public static final double MINIMUM_DIRECTION = 0; - - /** - * The currently used minimum scale factor to clamp to when a quick zoom gesture occurs - * - * @deprecated unused - */ - @Deprecated - public static final float MINIMUM_SCALE_FACTOR_CLAMP = 0.00f; - - /** - * The currently used maximum scale factor to clamp to when a quick zoom gesture occurs - * - * @deprecated unused - */ - @Deprecated - public static final float MAXIMUM_SCALE_FACTOR_CLAMP = 0.15f; - - /** - * Zoom value multiplier for scale gestures. - */ - public static final float ZOOM_RATE = 0.65f; - - /** - * Fragment Argument Key for MapboxMapOptions - */ - public static final String FRAG_ARG_MAPBOXMAPOPTIONS = "MapboxMapOptions"; - - /** - * Layer Id of annotations layer - */ - public static final String LAYER_ID_ANNOTATIONS = "com.mapbox.annotations.points"; - - // Save instance state keys - public static final String STATE_HAS_SAVED_STATE = "mapbox_savedState"; - public static final String STATE_CAMERA_POSITION = "mapbox_cameraPosition"; - public static final String STATE_ZOOM_ENABLED = "mapbox_zoomEnabled"; - public static final String STATE_SCROLL_ENABLED = "mapbox_scrollEnabled"; - public static final String STATE_ROTATE_ENABLED = "mapbox_rotateEnabled"; - public static final String STATE_TILT_ENABLED = "mapbox_tiltEnabled"; - public static final String STATE_DOUBLE_TAP_ENABLED = "mapbox_doubleTapEnabled"; - public static final String STATE_QUICK_ZOOM_ENABLED = "mapbox_quickZoom"; - public static final String STATE_ZOOM_RATE = "mapbox_zoomRate"; - public static final String STATE_DEBUG_ACTIVE = "mapbox_debugActive"; - public static final String STATE_COMPASS_ENABLED = "mapbox_compassEnabled"; - public static final String STATE_COMPASS_GRAVITY = "mapbox_compassGravity"; - public static final String STATE_COMPASS_MARGIN_LEFT = "mapbox_compassMarginLeft"; - public static final String STATE_COMPASS_MARGIN_TOP = "mapbox_compassMarginTop"; - public static final String STATE_COMPASS_MARGIN_RIGHT = "mapbox_compassMarginRight"; - public static final String STATE_COMPASS_MARGIN_BOTTOM = "mapbox_compassMarginBottom"; - public static final String STATE_COMPASS_FADE_WHEN_FACING_NORTH = "mapbox_compassFade"; - public static final String STATE_COMPASS_IMAGE_BITMAP = "mapbox_compassImage"; - public static final String STATE_LOGO_GRAVITY = "mapbox_logoGravity"; - public static final String STATE_LOGO_MARGIN_LEFT = "mapbox_logoMarginLeft"; - public static final String STATE_LOGO_MARGIN_TOP = "mapbox_logoMarginTop"; - public static final String STATE_LOGO_MARGIN_RIGHT = "mapbox_logoMarginRight"; - public static final String STATE_LOGO_MARGIN_BOTTOM = "mapbox_logoMarginBottom"; - public static final String STATE_LOGO_ENABLED = "mapbox_logoEnabled"; - public static final String STATE_ATTRIBUTION_GRAVITY = "mapbox_attrGravity"; - public static final String STATE_ATTRIBUTION_MARGIN_LEFT = "mapbox_attrMarginLeft"; - public static final String STATE_ATTRIBUTION_MARGIN_TOP = "mapbox_attrMarginTop"; - public static final String STATE_ATTRIBUTION_MARGIN_RIGHT = "mapbox_attrMarginRight"; - public static final String STATE_ATTRIBUTION_MARGIN_BOTTOM = "mapbox_atrrMarginBottom"; - public static final String STATE_ATTRIBUTION_ENABLED = "mapbox_atrrEnabled"; - public static final String STATE_DESELECT_MARKER_ON_TAP = "mapbox_deselectMarkerOnTap"; - public static final String STATE_USER_FOCAL_POINT = "mapbox_userFocalPoint"; - public static final String STATE_SCALE_ANIMATION_ENABLED = "mapbox_scaleAnimationEnabled"; - public static final String STATE_ROTATE_ANIMATION_ENABLED = "mapbox_rotateAnimationEnabled"; - public static final String STATE_FLING_ANIMATION_ENABLED = "mapbox_flingAnimationEnabled"; - public static final String STATE_INCREASE_ROTATE_THRESHOLD = "mapbox_increaseRotateThreshold"; - public static final String STATE_DISABLE_ROTATE_WHEN_SCALING = "mapbox_disableRotateWhenScaling"; - public static final String STATE_INCREASE_SCALE_THRESHOLD = "mapbox_increaseScaleThreshold"; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/TelemetryConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/TelemetryConstants.java deleted file mode 100644 index 3dbae9de4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/TelemetryConstants.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.mapbox.mapboxsdk.constants; - -@Deprecated -public class TelemetryConstants { - - public static final String TWO_FINGER_TAP = "TwoFingerTap"; - public static final String DOUBLE_TAP = "DoubleTap"; - public static final String SINGLE_TAP = "SingleTap"; - public static final String PAN = "Pan"; - public static final String PINCH = "Pinch"; - public static final String ROTATION = "Rotation"; - public static final String PITCH = "Pitch"; - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/package-info.java deleted file mode 100644 index 165394c03..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/constants/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Constant API classes. - */ -package com.mapbox.mapboxsdk.constants; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/CalledFromWorkerThreadException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/CalledFromWorkerThreadException.java deleted file mode 100644 index 32e852d31..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/CalledFromWorkerThreadException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -public class CalledFromWorkerThreadException extends RuntimeException { - - public CalledFromWorkerThreadException(String message) { - super(message); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java deleted file mode 100644 index 0d16c53b8..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/ConversionException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -/** - * A ConversionException is thrown when a conversion failed to execute. - */ -public class ConversionException extends RuntimeException { - - public ConversionException(String detailMessage) { - super(detailMessage); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java deleted file mode 100644 index 1f6b0efc4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/IconBitmapChangedException.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.maps.MapView; - -/** - *

- * An IconBitmapChangedException is thrown by MapView when a Marker is added - * that has an Icon with a Bitmap that has been modified since the creation of the Icon. - *

- * You cannot modify a {@code Icon} after it has been added to the map in a {@code Marker} - * - * @see MapView - * @see Icon - * @see Marker - */ -public class IconBitmapChangedException extends RuntimeException { - - public IconBitmapChangedException() { - super("The added Marker has an Icon with a bitmap that has been modified. An Icon cannot be modified" - + "after it has been added to the map in a Marker."); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java deleted file mode 100644 index c1d038581..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidLatLngBoundsException.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -/** - * An InvalidLatLngBoundsException is thrown by LatLngBounds - * when there aren't enough LatLng to create a bounds. - */ -public class InvalidLatLngBoundsException extends RuntimeException { - - public InvalidLatLngBoundsException(int latLngsListSize) { - super("Cannot create a LatLngBounds from " + latLngsListSize + " items"); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java deleted file mode 100644 index 44ee83265..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/InvalidMarkerPositionException.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -/** - * A InvalidMarkerPositionException is thrown when a Marker object is created with an invalid LatLng position. - */ -public class InvalidMarkerPositionException extends RuntimeException { - - /** - * Creates a invalid marker position exception thrown when a Marker object is created with an invalid LatLng position. - */ - public InvalidMarkerPositionException() { - super("Adding an invalid Marker to a Map. " - + "Missing the required position field. " - + "Provide a non null LatLng as position to the Marker."); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java deleted file mode 100644 index 431de85f0..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/MapboxConfigurationException.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -import android.content.Context; -import android.support.annotation.NonNull; - -/** - * A MapboxConfigurationException is thrown by MapboxMap when the SDK hasn't been properly initialised. - *

- * This occurs either when {@link com.mapbox.mapboxsdk.Mapbox} is not correctly initialised or the provided access token - * through {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)} isn't valid. - *

- * - * @see com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String) - */ -public class MapboxConfigurationException extends RuntimeException { - - /** - * Creates a Mapbox configuration exception thrown by MapboxMap when the SDK hasn't been properly initialised. - */ - public MapboxConfigurationException() { - super("\nUsing MapView requires calling Mapbox.getInstance(Context context, String accessToken) before " - + "inflating or creating the view. The access token parameter is required when using a Mapbox service." - + "\nPlease see https://www.mapbox.com/help/create-api-access-token/ to learn how to create one." - + "\nMore information in this guide https://www.mapbox.com/help/first-steps-android-sdk/#access-tokens."); - } - - /** - * Creates a Mapbox configuration exception thrown by MapboxMap when the SDK hasn't been properly initialised. - */ - public MapboxConfigurationException(@NonNull String message) { - super(message); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java deleted file mode 100644 index bffc10dc0..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/TooManyIconsException.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mapbox.mapboxsdk.exceptions; - -import com.mapbox.mapboxsdk.annotations.IconFactory; - -/** - *

- * A TooManyIconsException is thrown by IconFactory when it - * cannot create a Icon because there are already too many icons created. - *

- * You should try to reuse Icon objects whenever possible. - * - * @see IconFactory - */ -public class TooManyIconsException extends RuntimeException { - - public TooManyIconsException() { - super("Cannot create an Icon because there are already too many. Try reusing Icon objects whenever possible."); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/package-info.java deleted file mode 100644 index 09ecd55c1..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/exceptions/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Exception API classes. - */ -package com.mapbox.mapboxsdk.exceptions; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java deleted file mode 100644 index ce12489b4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLng.java +++ /dev/null @@ -1,334 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.location.Location; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.FloatRange; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.constants.GeometryConstants; -import com.mapbox.turf.TurfMeasurement; - -import static com.mapbox.turf.TurfConstants.UNIT_METRES; - - -/** - * A geographical location which contains a single latitude, longitude pair, with - * optional altitude. - *

- * Latitude and longitude are expressed as decimal degrees - * in the WGS84 datum. By default, altitude is 0.0, or sea level. - *

- *

- * Mapbox GL displays maps in the Mercator Projection and projects geographical - * data automatically, so all data enters in the WGS84 datum. - *

- */ -public class LatLng implements Parcelable { - - /** - * Inner class responsible for recreating Parcels into objects. - */ - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public LatLng createFromParcel(@NonNull Parcel in) { - return new LatLng(in); - } - - public LatLng[] newArray(int size) { - return new LatLng[size]; - } - }; - - @Keep - private double latitude; - @Keep - private double longitude; - private double altitude = 0.0; - - /** - * Construct a new latitude, longitude point at (0, 0) - */ - public LatLng() { - this.latitude = 0.0; - this.longitude = 0.0; - } - - /** - * Construct a new latitude, longitude point given double arguments - * - * @param latitude Latitude in degrees - * @param longitude Longitude in degrees - */ - @Keep - public LatLng(double latitude, double longitude) { - setLatitude(latitude); - setLongitude(longitude); - } - - /** - * Construct a new latitude, longitude, altitude point given double arguments - * - * @param latitude Latitude in degrees - * @param longitude Longitude in degress - * @param altitude Altitude in meters - */ - public LatLng(double latitude, double longitude, double altitude) { - setLatitude(latitude); - setLongitude(longitude); - setAltitude(altitude); - } - - /** - * Construct a new latitude, longitude, altitude point given location argument - * - * @param location Android Location - */ - public LatLng(Location location) { - this(location.getLatitude(), location.getLongitude(), location.getAltitude()); - } - - /** - * Construct a new latitude, longitude, altitude point given another latitude, longitude, altitude point. - * - * @param latLng LatLng to be cloned. - */ - public LatLng(LatLng latLng) { - this.latitude = latLng.latitude; - this.longitude = latLng.longitude; - this.altitude = latLng.altitude; - } - - /** - * Constructs a new latitude, longitude, altitude tuple given a parcel. - * - * @param in the parcel containing the latitude, longitude, altitude values - */ - protected LatLng(Parcel in) { - setLatitude(in.readDouble()); - setLongitude(in.readDouble()); - setAltitude(in.readDouble()); - } - - /** - * Set the latitude, in degrees. - *

- * This value is in the range of [-90, 90], see {@link GeometryConstants#MIN_LATITUDE} and - * {@link GeometryConstants#MAX_LATITUDE} - *

- * - * @param latitude the latitude value in degrees - * @see GeometryConstants#MIN_LATITUDE - * @see GeometryConstants#MAX_LATITUDE - */ - public void setLatitude( - @FloatRange(from = GeometryConstants.MIN_LATITUDE, to = GeometryConstants.MAX_LATITUDE) double latitude) { - if (Double.isNaN(latitude)) { - throw new IllegalArgumentException("latitude must not be NaN"); - } - if (Math.abs(latitude) > GeometryConstants.MAX_LATITUDE) { - throw new IllegalArgumentException("latitude must be between -90 and 90"); - } - this.latitude = latitude; - } - - /** - * Get the latitude, in degrees. - *

- * This value is in the range of [-90, 90], see {@link GeometryConstants#MIN_LATITUDE} and - * {@link GeometryConstants#MAX_LATITUDE} - *

- * - * @return the latitude value in degrees - * @see GeometryConstants#MIN_LATITUDE - * @see GeometryConstants#MAX_LATITUDE - */ - public double getLatitude() { - return latitude; - } - - /** - * Set the longitude, in degrees. - *

- * This value is in the range of [-180, 180], see {@link GeometryConstants#MIN_LONGITUDE} and - * {@link GeometryConstants#MAX_LONGITUDE} - *

- * - * @param longitude the longitude value in degrees - * @see GeometryConstants#MIN_LONGITUDE - * @see GeometryConstants#MAX_LONGITUDE - */ - public void setLongitude(@FloatRange(from = GeometryConstants.MIN_LONGITUDE, to = GeometryConstants.MAX_LONGITUDE) - double longitude) { - if (Double.isNaN(longitude)) { - throw new IllegalArgumentException("longitude must not be NaN"); - } - if (Double.isInfinite(longitude)) { - throw new IllegalArgumentException("longitude must not be infinite"); - } - this.longitude = longitude; - } - - /** - * Get the longitude, in degrees. - *

- * This value is in the range of [-180, 180], see {@link GeometryConstants#MIN_LONGITUDE} and - * {@link GeometryConstants#MAX_LONGITUDE} - *

- * - * @return the longitude value in degrees - * @see GeometryConstants#MIN_LONGITUDE - * @see GeometryConstants#MAX_LONGITUDE - */ - public double getLongitude() { - return longitude; - } - - /** - * Set the altitude, in meters. - * - * @param altitude the altitude in meters - */ - public void setAltitude(double altitude) { - this.altitude = altitude; - } - - /** - * Get the altitude, in meters. - * - * @return the altitude value in meters - */ - public double getAltitude() { - return altitude; - } - - /** - * Return a new LatLng object with a wrapped Longitude. This allows original data object - * to remain unchanged. - * - * @return new LatLng object with wrapped Longitude - */ - @NonNull - public LatLng wrap() { - return new LatLng(latitude, wrap(longitude, - GeometryConstants.MIN_WRAP_LONGITUDE, GeometryConstants.MAX_WRAP_LONGITUDE) - ); - } - - - /** - * Constrains value to the given range (including min & max) via modular arithmetic. - *

- * Same formula as used in Core GL (wrap.hpp) - * std::fmod((std::fmod((value - min), d) + d), d) + min; - *

- *

- * Multiples of max value will be wrapped to max. - *

- * - * @param value Value to wrap - * @param min Minimum value - * @param max Maximum value - * @return Wrapped value - */ - static double wrap(double value, double min, double max) { - double delta = max - min; - - double firstMod = (value - min) % delta; - double secondMod = (firstMod + delta) % delta; - - if (value >= max && secondMod == 0) { - return max; - } - return secondMod + min; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param object The object to compare - * @return True if equal, false if not - */ - @Override - public boolean equals(@Nullable Object object) { - if (this == object) { - return true; - } - if (object == null || getClass() != object.getClass()) { - return false; - } - - LatLng latLng = (LatLng) object; - - return Double.compare(latLng.altitude, altitude) == 0 && Double.compare(latLng.latitude, latitude) == 0 - && Double.compare(latLng.longitude, longitude) == 0; - } - - /** - * Returns a hash code value for the object. - * - * @return the hash code value - */ - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(latitude); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(longitude); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(altitude); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - /** - * Returns a string representation of the object. - * - * @return the string representation - */ - @NonNull - @Override - public String toString() { - return "LatLng [latitude=" + latitude + ", longitude=" + longitude + ", altitude=" + altitude + "]"; - } - - /** - * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation. - * - * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written - */ - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeDouble(latitude); - out.writeDouble(longitude); - out.writeDouble(altitude); - } - - /** - * Calculate distance between two points - * - * @param other Other LatLng to compare to - * @return distance in meters - */ - public double distanceTo(@NonNull LatLng other) { - return TurfMeasurement.distance( - Point.fromLngLat(longitude, latitude), - Point.fromLngLat(other.getLongitude(), other.getLatitude()), - UNIT_METRES - ); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java deleted file mode 100644 index 263780f47..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ /dev/null @@ -1,608 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.FloatRange; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.constants.GeometryConstants; -import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException; - -import java.util.ArrayList; -import java.util.List; - -import static com.mapbox.mapboxsdk.constants.GeometryConstants.MAX_LATITUDE; -import static com.mapbox.mapboxsdk.constants.GeometryConstants.MAX_LONGITUDE; -import static com.mapbox.mapboxsdk.constants.GeometryConstants.MAX_WRAP_LONGITUDE; -import static com.mapbox.mapboxsdk.constants.GeometryConstants.MIN_LATITUDE; -import static com.mapbox.mapboxsdk.constants.GeometryConstants.MIN_LONGITUDE; -import static com.mapbox.mapboxsdk.constants.GeometryConstants.MIN_WRAP_LONGITUDE; - -/** - * A geographical area representing a latitude/longitude aligned rectangle. - *

- * This class does not wrap values to the world bounds. - *

- */ -public class LatLngBounds implements Parcelable { - - @Keep - private final double latitudeNorth; - @Keep - private final double latitudeSouth; - @Keep - private final double longitudeEast; - @Keep - private final double longitudeWest; - - /** - * Construct a new LatLngBounds based on its corners, given in NESW - * order. - *

- * @since 7.0.0 LatLngBounds cannot be wrapped any more, i.e longitudeWest has to be - * less or equal to longitudeEast. - * - * For example, to represent bounds spanning 20 degrees crossing antimeridian with - * the NE point as (10, -170) and the SW point as (-10, 170), - * use (10, -190) and (-10, -170), or (10, -170) and (-10, -150). - * - * @param northLatitude Northern Latitude - * @param eastLongitude Eastern Longitude - * @param southLatitude Southern Latitude - * @param westLongitude Western Longitude - */ - @Keep - LatLngBounds(final double northLatitude, final double eastLongitude, - final double southLatitude, final double westLongitude) { - this.latitudeNorth = northLatitude; - this.longitudeEast = eastLongitude; - this.latitudeSouth = southLatitude; - this.longitudeWest = westLongitude; - } - - /** - * Returns the world bounds. - * - * @return the bounds representing the world - */ - public static LatLngBounds world() { - return LatLngBounds.from( - MAX_LATITUDE, MAX_WRAP_LONGITUDE, - MIN_LATITUDE, MIN_WRAP_LONGITUDE); - } - - /** - * Calculates the centerpoint of this LatLngBounds by simple interpolation and returns - * it as a point. This is a non-geodesic calculation which is not the geographic center. - * - * @return LatLng center of this LatLngBounds - */ - @NonNull - public LatLng getCenter() { - double latCenter = (this.latitudeNorth + this.latitudeSouth) / 2.0; - double longCenter = (this.longitudeEast + this.longitudeWest) / 2.0; - - return new LatLng(latCenter, longCenter); - } - - /** - * Get the north latitude value of this bounds. - * - * @return double latitude value for north - */ - public double getLatNorth() { - return this.latitudeNorth; - } - - /** - * Get the south latitude value of this bounds. - * - * @return double latitude value for south - */ - public double getLatSouth() { - return this.latitudeSouth; - } - - /** - * Get the east longitude value of this bounds. - * - * @return double longitude value for east - */ - public double getLonEast() { - return this.longitudeEast; - } - - /** - * Get the west longitude value of this bounds. - * - * @return double longitude value for west - */ - public double getLonWest() { - return this.longitudeWest; - } - - /** - * Get the latitude-longitude pair of the south west corner of this bounds. - * - * @return LatLng of the south west corner - */ - @NonNull - public LatLng getSouthWest() { - return new LatLng(latitudeSouth, longitudeWest); - } - - /** - * Get the latitude-longitude paur if the north east corner of this bounds. - * - * @return LatLng of the north east corner - */ - @NonNull - public LatLng getNorthEast() { - return new LatLng(latitudeNorth, longitudeEast); - } - - /** - * Get the latitude-longitude pair of the south east corner of this bounds. - * - * @return LatLng of the south east corner - */ - @NonNull - public LatLng getSouthEast() { - return new LatLng(latitudeSouth, longitudeEast); - } - - /** - * Get the latitude-longitude pair of the north west corner of this bounds. - * - * @return LatLng of the north west corner - */ - @NonNull - public LatLng getNorthWest() { - return new LatLng(latitudeNorth, longitudeWest); - } - - /** - * Get the area spanned by this LatLngBounds - * - * @return LatLngSpan area - */ - @NonNull - public LatLngSpan getSpan() { - return new LatLngSpan(getLatitudeSpan(), getLongitudeSpan()); - } - - /** - * Get the absolute distance, in degrees, between the north and - * south boundaries of this LatLngBounds - * - * @return Span distance - */ - public double getLatitudeSpan() { - return Math.abs(this.latitudeNorth - this.latitudeSouth); - } - - /** - * Get the absolute distance, in degrees, between the west and - * east boundaries of this LatLngBounds - * - * @return Span distance - */ - public double getLongitudeSpan() { - return Math.abs(this.longitudeEast - this.longitudeWest); - } - - /** - * Validate if LatLngBounds is empty, determined if absolute distance is - * - * @return boolean indicating if span is empty - */ - public boolean isEmptySpan() { - return getLongitudeSpan() == 0.0 || getLatitudeSpan() == 0.0; - } - - /** - * Returns a string representaton of the object. - * - * @return the string representation - */ - @NonNull - @Override - public String toString() { - return "N:" + this.latitudeNorth + "; E:" + this.longitudeEast + "; S:" + this.latitudeSouth - + "; W:" + this.longitudeWest; - } - - /** - * Constructs a LatLngBounds that contains all of a list of LatLng - * objects. Empty lists will yield invalid LatLngBounds. - * - * @param latLngs List of LatLng objects - * @return LatLngBounds - */ - static LatLngBounds fromLatLngs(final List latLngs) { - double minLat = MAX_LATITUDE; - double minLon = MAX_LONGITUDE; - double maxLat = MIN_LATITUDE; - double maxLon = MIN_LONGITUDE; - - for (final LatLng gp : latLngs) { - final double latitude = gp.getLatitude(); - final double longitude = gp.getLongitude(); - minLat = Math.min(minLat, latitude); - minLon = Math.min(minLon, longitude); - maxLat = Math.max(maxLat, latitude); - maxLon = Math.max(maxLon, longitude); - } - - return new LatLngBounds(maxLat, maxLon, minLat, minLon); - } - - /** - * Return an array of LatLng objects resembling this bounds. - * - * @return an array of 2 LatLng objects. - */ - @NonNull - public LatLng[] toLatLngs() { - return new LatLng[] {getNorthEast(), getSouthWest()}; - } - - /** - * Constructs a LatLngBounds from doubles representing a LatLng pair. - *

- * This values of latNorth and latSouth should be in the range of [-90, 90], - * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, - * otherwise IllegalArgumentException will be thrown. - * latNorth should be greater or equal latSouth, otherwise IllegalArgumentException will be thrown. - *

- * This method doesn't recalculate most east or most west boundaries. - * Note @since 7.0.0 lonEast and lonWest will NOT be wrapped to be in the range of [-180, 180], - * see {@link GeometryConstants#MIN_LONGITUDE} and {@link GeometryConstants#MAX_LONGITUDE} - * lonEast should be greater or equal lonWest, otherwise IllegalArgumentException will be thrown. - *

- */ - public static LatLngBounds from( - @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latNorth, - double lonEast, - @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latSouth, - double lonWest) { - - checkParams(latNorth, lonEast, latSouth, lonWest); - - return new LatLngBounds(latNorth, lonEast, latSouth, lonWest); - } - - private static void checkParams( - @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latNorth, - double lonEast, - @FloatRange(from = MIN_LATITUDE, to = MAX_LATITUDE) double latSouth, - double lonWest) { - - if (Double.isNaN(latNorth) || Double.isNaN(latSouth)) { - throw new IllegalArgumentException("latitude must not be NaN"); - } - - if (Double.isNaN(lonEast) || Double.isNaN(lonWest)) { - throw new IllegalArgumentException("longitude must not be NaN"); - } - - if (Double.isInfinite(lonEast) || Double.isInfinite(lonWest)) { - throw new IllegalArgumentException("longitude must not be infinite"); - } - - if (latNorth > MAX_LATITUDE || latNorth < MIN_LATITUDE - || latSouth > MAX_LATITUDE || latSouth < MIN_LATITUDE) { - throw new IllegalArgumentException("latitude must be between -90 and 90"); - } - - if (latNorth < latSouth) { - throw new IllegalArgumentException("latNorth cannot be less than latSouth"); - } - - if (lonEast < lonWest) { - throw new IllegalArgumentException("lonEast cannot be less than lonWest"); - } - } - - private static double lat_(int z, int y) { - double n = Math.PI - 2.0 * Math.PI * y / Math.pow(2.0, z); - return Math.toDegrees(Math.atan(0.5 * (Math.exp(n) - Math.exp(-n)))); - } - - private static double lon_(int z, int x) { - return x / Math.pow(2.0, z) * 360.0 - GeometryConstants.MAX_WRAP_LONGITUDE; - } - - /** - * Constructs a LatLngBounds from a Tile identifier. - *

- * Returned bounds will have latitude in the range of Mercator projection. - * - * @param z Tile zoom level. - * @param x Tile X coordinate. - * @param y Tile Y coordinate. - * @see GeometryConstants#MIN_MERCATOR_LATITUDE - * @see GeometryConstants#MAX_MERCATOR_LATITUDE - */ - public static LatLngBounds from(int z, int x, int y) { - return new LatLngBounds(lat_(z, y), lon_(z, x + 1), lat_(z, y + 1), lon_(z, x)); - } - - /** - * Constructs a LatLngBounds from current bounds with an additional latitude-longitude pair. - * - * @param latLng the latitude lognitude pair to include in the bounds. - * @return the newly constructed bounds - */ - @NonNull - public LatLngBounds include(@NonNull LatLng latLng) { - return new LatLngBounds.Builder() - .include(getNorthEast()) - .include(getSouthWest()) - .include(latLng) - .build(); - } - - /** - * Determines whether this LatLngBounds matches another one via LatLng. - * - * @param o another object - * @return a boolean indicating whether the LatLngBounds are equal - */ - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o instanceof LatLngBounds) { - LatLngBounds other = (LatLngBounds) o; - return latitudeNorth == other.getLatNorth() - && latitudeSouth == other.getLatSouth() - && longitudeEast == other.getLonEast() - && longitudeWest == other.getLonWest(); - } - return false; - } - - - private boolean containsLatitude(final double latitude) { - return (latitude <= this.latitudeNorth) - && (latitude >= this.latitudeSouth); - } - - private boolean containsLongitude(final double longitude) { - return (longitude <= this.longitudeEast) - && (longitude >= this.longitudeWest); - } - - /** - * Determines whether this LatLngBounds contains a point. - * - * @param latLng the point which may be contained - * @return true, if the point is contained within the bounds - */ - public boolean contains(@NonNull final LatLng latLng) { - return containsLatitude(latLng.getLatitude()) - && containsLongitude(latLng.getLongitude()); - } - - /** - * Determines whether this LatLngBounds contains another bounds. - * - * @param other the bounds which may be contained - * @return true, if the bounds is contained within the bounds - */ - public boolean contains(@NonNull final LatLngBounds other) { - return contains(other.getNorthEast()) - && contains(other.getSouthWest()); - } - - /** - * Returns a new LatLngBounds that stretches to contain both this and another LatLngBounds. - * - * @param bounds LatLngBounds to add - * @return LatLngBounds - */ - @NonNull - public LatLngBounds union(@NonNull LatLngBounds bounds) { - return unionNoParamCheck(bounds.getLatNorth(), bounds.getLonEast(), - bounds.getLatSouth(), bounds.getLonWest()); - } - - /** - * Returns a new LatLngBounds that stretches to contain both this and another LatLngBounds. - * - *

- * This values of northLat and southLat should be in the range of [-90, 90], - * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, - * otherwise IllegalArgumentException will be thrown. - * northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown. - * - *

- * eastLon should be greater or equal westLon, otherwise IllegalArgumentException will be thrown. - * - * @param northLat Northern Latitude corner point - * @param eastLon Eastern Longitude corner point - * @param southLat Southern Latitude corner point - * @param westLon Western Longitude corner point - * @return LatLngBounds - */ - @NonNull - public LatLngBounds union(final double northLat, final double eastLon, - final double southLat, final double westLon) { - checkParams(northLat, eastLon, southLat, westLon); - return unionNoParamCheck(northLat, eastLon, southLat, westLon); - } - - private LatLngBounds unionNoParamCheck(final double northLat, final double eastLon, - final double southLat, final double westLon) { - - return new LatLngBounds((this.latitudeNorth < northLat) ? northLat : this.latitudeNorth, - (this.longitudeEast < eastLon) ? eastLon : this.longitudeEast, - (this.latitudeSouth > southLat) ? southLat : this.latitudeSouth, - (this.longitudeWest > westLon) ? westLon : this.longitudeWest); - } - - /** - * Returns a new LatLngBounds that is the intersection of this with another LatLngBounds, - * - * @param box LatLngBounds to intersect with - * @return LatLngBounds - */ - @Nullable - public LatLngBounds intersect(@NonNull LatLngBounds box) { - return intersectNoParamCheck(box.getLatNorth(), box.getLonEast(), box.getLatSouth(), box.getLonWest()); - } - - - /** - * Returns a new LatLngBounds that is the intersection of this with another box. - * - *

- * This values of northLat and southLat should be in the range of [-90, 90], - * see {@link GeometryConstants#MIN_LATITUDE} and {@link GeometryConstants#MAX_LATITUDE}, - * otherwise IllegalArgumentException will be thrown. - * northLat should be greater or equal southLat, otherwise IllegalArgumentException will be thrown. - * - *

- * eastLon should be greater or equal westLon, otherwise IllegalArgumentException will be thrown. - * - * @param northLat Northern Latitude corner point - * @param eastLon Eastern Longitude corner point - * @param southLat Southern Latitude corner point - * @param westLon Western Longitude corner point - * @return LatLngBounds - */ - @NonNull - public LatLngBounds intersect(final double northLat, final double eastLon, - final double southLat, final double westLon) { - checkParams(northLat, eastLon, southLat, westLon); - return intersectNoParamCheck(northLat, eastLon, southLat, westLon); - } - - private LatLngBounds intersectNoParamCheck(final double northLat, final double eastLon, - final double southLat, final double westLon) { - - double minLonWest = Math.max(this.longitudeWest, westLon); - double maxLonEast = Math.min(this.longitudeEast, eastLon); - if (maxLonEast >= minLonWest) { - double minLatSouth = Math.max(this.latitudeSouth, southLat); - double maxLatNorth = Math.min(this.latitudeNorth, northLat); - if (maxLatNorth >= minLatSouth) { - return new LatLngBounds(maxLatNorth, maxLonEast, minLatSouth, minLonWest); - } - } - return null; - } - - - - /** - * Inner class responsible for recreating Parcels into objects. - */ - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public LatLngBounds createFromParcel(@NonNull final Parcel in) { - return readFromParcel(in); - } - - @Override - public LatLngBounds[] newArray(final int size) { - return new LatLngBounds[size]; - } - }; - - /** - * Returns a hash code value for the object. - * - * @return the hash code - */ - @Override - public int hashCode() { - return (int) ((latitudeNorth + 90) - + ((latitudeSouth + 90) * 1000) - + ((longitudeEast + 180) * 1000000) - + ((longitudeWest + 180) * 1000000000)); - } - - /** - * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation. - * - * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written - */ - @Override - public void writeToParcel(@NonNull final Parcel out, final int flags) { - out.writeDouble(this.latitudeNorth); - out.writeDouble(this.longitudeEast); - out.writeDouble(this.latitudeSouth); - out.writeDouble(this.longitudeWest); - } - - private static LatLngBounds readFromParcel(final Parcel in) { - final double northLat = in.readDouble(); - final double eastLon = in.readDouble(); - final double southLat = in.readDouble(); - final double westLon = in.readDouble(); - return new LatLngBounds(northLat, eastLon, southLat, westLon); - } - - /** - * Builder for composing LatLngBounds objects. - */ - public static final class Builder { - - private final List latLngList = new ArrayList<>(); - - /** - * Builds a new LatLngBounds. - *

- * Throws an {@link InvalidLatLngBoundsException} when no LatLngBounds can be created. - *

- * - * @return the build LatLngBounds - */ - public LatLngBounds build() { - if (latLngList.size() < 2) { - throw new InvalidLatLngBoundsException(latLngList.size()); - } - return LatLngBounds.fromLatLngs(latLngList); - } - - /** - * Adds a LatLng object to the LatLngBounds.Builder. - * - * @param latLngs the List of LatLng objects to be added - * @return this - */ - @NonNull - public Builder includes(@NonNull List latLngs) { - latLngList.addAll(latLngs); - return this; - } - - /** - * Adds a LatLng object to the LatLngBounds.Builder. - * - * @param latLng the LatLng to be added - * @return this - */ - @NonNull - public Builder include(@NonNull LatLng latLng) { - latLngList.add(latLng); - return this; - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java deleted file mode 100644 index d17d0d6fe..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngQuad.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; - -/** - * A geographical area representing a non-aligned quadrilateral - *

- * This class does not wrap values to the world bounds - *

- */ -public class LatLngQuad implements Parcelable { - - @Keep - private final LatLng topLeft; - @Keep - private final LatLng topRight; - @Keep - private final LatLng bottomRight; - @Keep - private final LatLng bottomLeft; - - /** - * Construct a new LatLngQuad based on its corners, - * in order top left, top right, bottom left, bottom right - */ - @Keep - public LatLngQuad(final LatLng topLeft, final LatLng topRight, final LatLng bottomRight, final LatLng bottomLeft) { - this.topLeft = topLeft; - this.topRight = topRight; - this.bottomRight = bottomRight; - this.bottomLeft = bottomLeft; - } - - public LatLng getTopLeft() { - return this.topLeft; - } - - public LatLng getTopRight() { - return this.topRight; - } - - public LatLng getBottomRight() { - return this.bottomRight; - } - - public LatLng getBottomLeft() { - return this.bottomLeft; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - @Override - public LatLngQuad createFromParcel(@NonNull final Parcel in) { - return readFromParcel(in); - } - - @Override - public LatLngQuad[] newArray(final int size) { - return new LatLngQuad[size]; - } - }; - - @Override - public int hashCode() { - int code = topLeft.hashCode(); - code = (code ^ code >>> 31) + topRight.hashCode(); - code = (code ^ code >>> 31) + bottomRight.hashCode(); - code = (code ^ code >>> 31) + bottomLeft.hashCode(); - return code; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull final Parcel out, final int arg1) { - topLeft.writeToParcel(out, arg1); - topRight.writeToParcel(out, arg1); - bottomRight.writeToParcel(out, arg1); - bottomLeft.writeToParcel(out, arg1); - } - - private static LatLngQuad readFromParcel(@NonNull final Parcel in) { - final LatLng topLeft = new LatLng(in); - final LatLng topRight = new LatLng(in); - final LatLng bottomRight = new LatLng(in); - final LatLng bottomLeft = new LatLng(in); - return new LatLngQuad(topLeft, topRight, bottomRight, bottomLeft); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java deleted file mode 100644 index 941e6652f..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngSpan.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.NonNull; - -/** - * A geographical span defined by its latitude and longitude span. - */ -public class LatLngSpan implements Parcelable { - - private double mLatitudeSpan; - private double mLongitudeSpan; - - private LatLngSpan(@NonNull Parcel in) { - mLatitudeSpan = in.readDouble(); - mLongitudeSpan = in.readDouble(); - } - - /** - * Creates a LatLgnSpan. - * - * @param latitudeSpan The span used for latitude. - * @param longitudeSpan The span used for longitude. - */ - public LatLngSpan(double latitudeSpan, double longitudeSpan) { - mLatitudeSpan = latitudeSpan; - mLongitudeSpan = longitudeSpan; - } - - /** - * Returns the latitude span. - * - * @return The latitude span. - */ - public double getLatitudeSpan() { - return mLatitudeSpan; - } - - /** - * Sets the latitude span. - * - * @param latitudeSpan The latitude span to set. - */ - public void setLatitudeSpan(double latitudeSpan) { - mLatitudeSpan = latitudeSpan; - } - - /** - * Returns to longitude span. - * - * @return The longitude span. - */ - public double getLongitudeSpan() { - return mLongitudeSpan; - } - - /** - * Sets the longitude span. - * - * @param longitudeSpan The longitude span to set. - */ - public void setLongitudeSpan(double longitudeSpan) { - mLongitudeSpan = longitudeSpan; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param object The object to compare - * @return True if equal, false if not - */ - @Override - public boolean equals(Object object) { - if (this == object) { - return true; - } - if (object instanceof LatLngSpan) { - LatLngSpan other = (LatLngSpan) object; - return mLongitudeSpan == other.getLongitudeSpan() - && mLatitudeSpan == other.getLatitudeSpan(); - } - return false; - } - - /** - * Inner class responsible for recreating Parcels into objects. - */ - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public LatLngSpan createFromParcel(@NonNull Parcel in) { - return new LatLngSpan(in); - } - - @Override - public LatLngSpan[] newArray(int size) { - return new LatLngSpan[size]; - } - }; - - /** - * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation. - * - * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out Parcel in which the object should be written - * @param flags Additional flags about how the object should be written - */ - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeDouble(mLatitudeSpan); - out.writeDouble(mLongitudeSpan); - } - - /** - * Returns a hash code value for the object. - * - * @return hash code value of this - */ - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(mLatitudeSpan); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(mLongitudeSpan); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java deleted file mode 100644 index 745a7c6be..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/ProjectedMeters.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -/** - * ProjectedMeters is a projection of longitude, latitude points in Mercator meters. - *

- * these have been projected into Mapbox GL's Mercator projection. Instead of decimal - * degrees, it uses Mercator meters (which are notably not equivalent to SI meters) - * except at the equator. - *

- */ -public class ProjectedMeters implements Parcelable { - - /** - * Inner class responsible for recreating Parcels into objects. - */ - public static final Creator CREATOR = new Creator() { - public ProjectedMeters createFromParcel(@NonNull Parcel in) { - return new ProjectedMeters(in); - } - - public ProjectedMeters[] newArray(int size) { - return new ProjectedMeters[size]; - } - }; - - private double northing; - private double easting; - - /** - * Creates a ProjectedMeters based on projected meters in north and east direction. - * - * @param northing the northing in meters - * @param easting the easting in meters - */ - @Keep - public ProjectedMeters(double northing, double easting) { - this.northing = northing; - this.easting = easting; - } - - /** - * Creates a ProjecteMeters based on another set of projected meters. - * - * @param projectedMeters The projected meters to be based on. - */ - public ProjectedMeters(ProjectedMeters projectedMeters) { - this.northing = projectedMeters.northing; - this.easting = projectedMeters.easting; - } - - /** - * Creates a ProjectedMeters from a Parcel. - * - * @param in The parcel to create from - * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance. - */ - private ProjectedMeters(Parcel in) { - northing = in.readDouble(); - easting = in.readDouble(); - } - - /** - * Get projected meters in north direction. - * - * @return Projected meters in north. - */ - public double getNorthing() { - return northing; - } - - /** - * Get projected meters in east direction. - * - * @return Projected meters in east. - */ - public double getEasting() { - return easting; - } - - /** - * Indicates whether some other object is "equal to" this one. - * - * @param other The object to compare this to - * @return true if equal, false if not - */ - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - - ProjectedMeters projectedMeters = (ProjectedMeters) other; - - return Double.compare(projectedMeters.easting, easting) == 0 - && Double.compare(projectedMeters.northing, northing) == 0; - } - - /** - * Returns a hash code value for the object. - * - * @return the hash code of this - */ - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(easting); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(northing); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - /** - * Returns a string representation of the object. - * - * @return the string representation of this - */ - @NonNull - @Override - public String toString() { - return "ProjectedMeters [northing=" + northing + ", easting=" + easting + "]"; - } - - /** - * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation. - * - * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written - */ - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeDouble(northing); - out.writeDouble(easting); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java deleted file mode 100644 index a8b4421ee..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java +++ /dev/null @@ -1,161 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.NonNull; - -/** - * Contains the four points defining the four-sided polygon that is visible in a map's camera. - * This polygon can be a trapezoid instead of a rectangle, because a camera can have tilt. - * If the camera is directly over the center of the camera, the shape is rectangular, - * but if the camera is tilted, the shape will appear to be a trapezoid whose - * smallest side is closest to the point of view. - */ -public class VisibleRegion implements Parcelable { - - /** - * LatLng object that defines the far left corner of the camera. - */ - public final LatLng farLeft; - - /** - * LatLng object that defines the far right corner of the camera. - */ - public final LatLng farRight; - - /** - * LatLng object that defines the bottom left corner of the camera. - */ - public final LatLng nearLeft; - - /** - * LatLng object that defines the bottom right corner of the camera. - */ - public final LatLng nearRight; - - /** - * The smallest bounding box that includes the visible region defined in this class. - */ - public final LatLngBounds latLngBounds; - - /** - * Creates a VisibleRegion from a Parcel. - * - * @param in The Parcel to create this from - */ - private VisibleRegion(Parcel in) { - this.farLeft = in.readParcelable(LatLng.class.getClassLoader()); - this.farRight = in.readParcelable(LatLng.class.getClassLoader()); - this.nearLeft = in.readParcelable(LatLng.class.getClassLoader()); - this.nearRight = in.readParcelable(LatLng.class.getClassLoader()); - this.latLngBounds = in.readParcelable(LatLngBounds.class.getClassLoader()); - } - - /** - * Creates a VisibleRegion given the four corners of the camera. - * - * @param farLeft A LatLng object containing the latitude and longitude of the near left corner of the region. - * @param farRight A LatLng object containing the latitude and longitude of the near left corner of the region. - * @param nearLeft A LatLng object containing the latitude and longitude of the near left corner of the region. - * @param nearRight A LatLng object containing the latitude and longitude of the near left corner of the region. - * @param latLngBounds The smallest bounding box that includes the visible region defined in this class. - */ - public VisibleRegion(LatLng farLeft, LatLng farRight, LatLng nearLeft, LatLng nearRight, LatLngBounds latLngBounds) { - this.farLeft = farLeft; - this.farRight = farRight; - this.nearLeft = nearLeft; - this.nearRight = nearRight; - this.latLngBounds = latLngBounds; - } - - /** - * Compares this VisibleRegion to another object. - * If the other object is actually a pointer to this object, - * or if all four corners and the bounds of the two objects are the same, - * this method returns true. Otherwise, this method returns false. - * - * @param o The Object to compare with. - * @return true if both objects are the same object. - */ - @Override - public boolean equals(Object o) { - if (!(o instanceof VisibleRegion)) { - return false; - } - if (o == this) { - return true; - } - - VisibleRegion visibleRegion = (VisibleRegion) o; - return farLeft.equals(visibleRegion.farLeft) - && farRight.equals(visibleRegion.farRight) - && nearLeft.equals(visibleRegion.nearLeft) - && nearRight.equals(visibleRegion.nearRight) - && latLngBounds.equals(visibleRegion.latLngBounds); - } - - /** - * The string representation of the object. - * - * @return the string representation of this - */ - @NonNull - @Override - public String toString() { - return "[farLeft [" + farLeft + "], farRight [" + farRight + "], nearLeft [" + nearLeft + "], nearRight [" - + nearRight + "], latLngBounds [" + latLngBounds + "]]"; - } - - /** - * Returns a hash code value for the object. - * - * @return the hash code - */ - @Override - public int hashCode() { - return ((farLeft.hashCode() + 90) - + ((farRight.hashCode() + 90) * 1000) - + ((nearLeft.hashCode() + 180) * 1000000) - + ((nearRight.hashCode() + 180) * 1000000000)); - } - - /** - * Describe the kinds of special objects contained in this Parcelable instance's marshaled representation. - * - * @return a bitmask indicating the set of special object types marshaled by this Parcelable object instance. - */ - @Override - public int describeContents() { - return 0; - } - - /** - * Flatten this object in to a Parcel. - * - * @param out The Parcel in which the object should be written. - * @param flags Additional flags about how the object should be written - */ - @Override - public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeParcelable(farLeft, flags); - out.writeParcelable(farRight, flags); - out.writeParcelable(nearLeft, flags); - out.writeParcelable(nearRight, flags); - out.writeParcelable(latLngBounds, flags); - } - - /** - * Inner class responsible for recreating Parcels into objects. - */ - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - public VisibleRegion createFromParcel(@NonNull Parcel in) { - return new VisibleRegion(in); - } - - public VisibleRegion[] newArray(int size) { - return new VisibleRegion[size]; - } - }; - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/package-info.java deleted file mode 100644 index 754af7659..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Geometry API classes. - */ -package com.mapbox.mapboxsdk.geometry; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpIdentifier.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpIdentifier.java deleted file mode 100644 index 5f4f85ea7..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpIdentifier.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.Mapbox; - -public class HttpIdentifier { - - private HttpIdentifier() { - } - - /** - * Returns the application identifier, consisting out the package name, version name and version code. - * - * @return the application identifier - */ - public static String getIdentifier() { - return getIdentifier(Mapbox.getApplicationContext()); - } - - /** - * Returns the application identifier, consisting out the package name, version name and version code. - * - * @param context the context used to retrieve the package manager from - * @return the application identifier - */ - private static String getIdentifier(@NonNull Context context) { - try { - PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - return String.format("%s/%s (%s)", context.getPackageName(), packageInfo.versionName, packageInfo.versionCode); - } catch (Exception exception) { - MapStrictMode.strictModeViolation(exception); - return ""; - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpLogger.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpLogger.java deleted file mode 100644 index 2f44694f0..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpLogger.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -import android.util.Log; -import com.mapbox.mapboxsdk.log.Logger; - -import static com.mapbox.mapboxsdk.http.HttpRequest.CONNECTION_ERROR; -import static com.mapbox.mapboxsdk.http.HttpRequest.TEMPORARY_ERROR; - -public class HttpLogger { - - private static final String TAG = "Mbgl-HttpRequest"; - - public static boolean logRequestUrl; - public static boolean logEnabled = true; - - private HttpLogger(){ - } - - public static void logFailure(int type, String errorMessage, String requestUrl) { - log(type == TEMPORARY_ERROR ? Log.DEBUG : type == CONNECTION_ERROR ? Log.INFO : Log.WARN, - String.format( - "Request failed due to a %s error: %s %s", - type == TEMPORARY_ERROR ? "temporary" : type == CONNECTION_ERROR ? "connection" : "permanent", - errorMessage, - logRequestUrl ? requestUrl : "" - ) - ); - } - - public static void log(int type, String errorMessage) { - if (logEnabled) { - Logger.log(type, TAG, errorMessage); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequest.java deleted file mode 100644 index 1fd4f43cd..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -/** - * Interface definition for performing http requests. - *

- * This allows to provide alternative implementations for the http interaction of this library. - *

- */ -public interface HttpRequest { - - int CONNECTION_ERROR = 0; - int TEMPORARY_ERROR = 1; - int PERMANENT_ERROR = 2; - - /** - * Executes the request. - * - * @param httpRequest callback to be invoked when we receive a response - * @param nativePtr the pointer associated to the request - * @param resourceUrl the resource url to download - * @param etag http header, identifier for a specific version of a resource - * @param modified http header, used to determine if a resource hasn't been modified since - * @param offlineUsage flag to indicate a resource will be used for offline, appends offline=true as a query parameter - */ - void executeRequest(HttpResponder httpRequest, long nativePtr, String resourceUrl, - String etag, String modified, boolean offlineUsage); - - /** - * Cancels the request. - */ - void cancelRequest(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java deleted file mode 100644 index 1faf8f5e5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpRequestUrl.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.Mapbox; - -public class HttpRequestUrl { - - private HttpRequestUrl() { - } - - /** - * Adapts a resource request url based on the host, query size, and offline requirement. - * Mapbox resources downloaded for offline use are subject to separate Vector Tile and - * Raster Tile API pricing and are not included in the Maps SDK’s “unlimited” requests. - * See our pricing page for more information. - * - * @param host the host used as endpoint - * @param resourceUrl the resource to download - * @param querySize the query size of the resource request - * @param offline the type of resource, either offline or online - * @return the adapted resource url - */ - public static String buildResourceUrl(@NonNull String host, String resourceUrl, int querySize, boolean offline) { - if (isValidMapboxEndpoint(host)) { - if (querySize == 0) { - resourceUrl = resourceUrl + "?"; - } else { - resourceUrl = resourceUrl + "&"; - } - // Only add SKU token to requests not tagged as "offline" usage. - if (offline) { - resourceUrl = resourceUrl + "offline=true"; - } else { - resourceUrl = resourceUrl + "sku=" + Mapbox.getSkuToken(); - } - } - return resourceUrl; - } - - /** - * Validates if the host used as endpoint is a valid Mapbox endpoint. - * - * @param host the host used as endpoint - * @return true if a valid Mapbox endpoint - */ - private static boolean isValidMapboxEndpoint(String host) { - return host.equals("mapbox.com") - || host.endsWith(".mapbox.com") - || host.equals("mapbox.cn") - || host.endsWith(".mapbox.cn"); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpResponder.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpResponder.java deleted file mode 100644 index a050e07c5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/HttpResponder.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -/** - * Interface definition for a callback to be invoked when either a response was returned for a requested resource or - * when an error occurred when requesting the resource. - */ -public interface HttpResponder { - - /** - * Invoked when a resource has finished. - * - * @param responseCode http response code - * @param eTag http header, identifier for a specific version of a resource - * @param lastModified http header, used to determine if a resource hasn't been modified since - * @param cacheControl http header, used to determine cache strategy of a resource - * @param expires http header, used to determine when a resource is stale - * @param retryAfter http header, used to indicate when the service is expected to be unavailable to the client - * @param xRateLimitReset http header, used to determine the remaining window before the rate limit resets - * @param body http response body, in an array of bytes representation - */ - void onResponse(int responseCode, String eTag, String lastModified, String cacheControl, String expires, - String retryAfter, String xRateLimitReset, byte[] body); - - /** - * Invoked when a resource failed to be retrieved. - * - * @param type the error type, either one of {@link HttpRequest#CONNECTION_ERROR}, - * {@link HttpRequest#TEMPORARY_ERROR} or {@link HttpRequest#PERMANENT_ERROR} - * @param errorMessage the error message associated with the failure - */ - void handleFailure(int type, String errorMessage); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/LocalRequestTask.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/LocalRequestTask.java deleted file mode 100644 index 2d9fe43ad..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/LocalRequestTask.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -import android.content.res.AssetManager; -import android.os.AsyncTask; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.log.Logger; - -import java.io.IOException; -import java.io.InputStream; - -class LocalRequestTask extends AsyncTask { - - private static final String TAG = "Mbgl-LocalRequestTask"; - - private OnLocalRequestResponse requestResponse; - - LocalRequestTask(OnLocalRequestResponse requestResponse) { - this.requestResponse = requestResponse; - } - - @Nullable - @Override - protected byte[] doInBackground(String... strings) { - return loadFile(Mapbox.getApplicationContext().getAssets(), - "integration/" + strings[0] - .substring(8) - .replaceAll("%20", " ") - .replaceAll("%2c", ",")); - } - - @Override - protected void onPostExecute(@Nullable byte[] bytes) { - super.onPostExecute(bytes); - if (bytes != null && requestResponse != null) { - requestResponse.onResponse(bytes); - } - } - - @Nullable - private static byte[] loadFile(AssetManager assets, @NonNull String path) { - byte[] buffer = null; - InputStream input = null; - try { - input = assets.open(path); - int size = input.available(); - buffer = new byte[size]; - input.read(buffer); - } catch (IOException exception) { - logFileError(exception); - } finally { - if (input != null) { - try { - input.close(); - } catch (IOException exception) { - logFileError(exception); - } - } - } - return buffer; - } - - private static void logFileError(Exception exception) { - String message = "Load file failed"; - Logger.e(TAG, message, exception); - MapStrictMode.strictModeViolation(message, exception); - } - - public interface OnLocalRequestResponse { - void onResponse(byte[] bytes); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/NativeHttpRequest.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/NativeHttpRequest.java deleted file mode 100644 index e59b07dc1..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/NativeHttpRequest.java +++ /dev/null @@ -1,90 +0,0 @@ -package com.mapbox.mapboxsdk.http; - -import android.support.annotation.Keep; -import android.support.annotation.Nullable; -import com.mapbox.mapboxsdk.Mapbox; - -import java.util.concurrent.locks.ReentrantLock; - -@Keep -public class NativeHttpRequest implements HttpResponder { - - private final HttpRequest httpRequest = Mapbox.getModuleProvider().createHttpRequest(); - - // Reentrancy is not needed, but "Lock" is an abstract class. - private final ReentrantLock lock = new ReentrantLock(); - - @Keep - private long nativePtr; - - @Keep - private NativeHttpRequest(long nativePtr, String resourceUrl, String etag, String modified, boolean offlineUsage) { - this.nativePtr = nativePtr; - - if (resourceUrl.startsWith("local://")) { - // used by render test to serve files from assets - executeLocalRequest(resourceUrl); - return; - } - httpRequest.executeRequest(this, nativePtr, resourceUrl, etag, modified, offlineUsage); - } - - public void cancel() { - httpRequest.cancelRequest(); - - // TODO: We need a lock here because we can try - // to cancel at the same time the request is getting - // answered on the OkHTTP thread. We could get rid of - // this lock by using Runnable when we move Android - // implementation of mbgl::RunLoop to Looper. - lock.lock(); - nativePtr = 0; - lock.unlock(); - } - - public void onResponse(int responseCode, String etag, String lastModified, String cacheControl, String expires, - String retryAfter, String xRateLimitReset, byte[] body) { - lock.lock(); - if (nativePtr != 0) { - nativeOnResponse(responseCode, - etag, - lastModified, - cacheControl, - expires, - retryAfter, - xRateLimitReset, - body); - } - lock.unlock(); - } - - private void executeLocalRequest(String resourceUrl) { - new LocalRequestTask(new LocalRequestTask.OnLocalRequestResponse() { - @Override - public void onResponse(@Nullable byte[] bytes) { - if (bytes != null) { - lock.lock(); - if (nativePtr != 0) { - NativeHttpRequest.this.nativeOnResponse(200, null, null, null, null, null, null, bytes); - } - lock.unlock(); - } - } - }).execute(resourceUrl); - } - - public void handleFailure(int type, String errorMessage) { - lock.lock(); - if (nativePtr != 0) { - nativeOnFailure(type, errorMessage); - } - lock.unlock(); - } - - @Keep - private native void nativeOnFailure(int type, String message); - - @Keep - private native void nativeOnResponse(int code, String etag, String modified, String cacheControl, String expires, - String retryAfter, String xRateLimitReset, byte[] body); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/package-info.java deleted file mode 100644 index 09eeda2b7..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/http/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Do not use this package. Internal use only. - */ -package com.mapbox.mapboxsdk.http; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/AnimatorListenerHolder.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/AnimatorListenerHolder.java deleted file mode 100644 index 5dcf30919..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/AnimatorListenerHolder.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -class AnimatorListenerHolder { - @MapboxAnimator.Type - private final int animatorType; - private final MapboxAnimator.AnimationsValueChangeListener listener; - - AnimatorListenerHolder(@MapboxAnimator.Type int animatorType, MapboxAnimator.AnimationsValueChangeListener listener) { - this.animatorType = animatorType; - this.listener = listener; - } - - @MapboxAnimator.Type - public int getAnimatorType() { - return animatorType; - } - - public MapboxAnimator.AnimationsValueChangeListener getListener() { - return listener; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - AnimatorListenerHolder that = (AnimatorListenerHolder) o; - - if (animatorType != that.animatorType) { - return false; - } - return listener != null ? listener.equals(that.listener) : that.listener == null; - } - - @Override - public int hashCode() { - int result = animatorType; - result = 31 * result + (listener != null ? listener.hashCode() : 0); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java deleted file mode 100644 index bb7fd4972..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassEngine.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.location.modes.CameraMode; -import com.mapbox.mapboxsdk.location.modes.RenderMode; - -/** - * Interface defining the source of compass heading data that is - * consumed by the {@link LocationComponent} when in compass related - * {@link RenderMode} or - * {@link CameraMode}s. - */ -public interface CompassEngine { - - /** - * Adds a {@link CompassListener} that can be used to - * receive heading and state changes. - * - * @param compassListener to be added - */ - void addCompassListener(@NonNull CompassListener compassListener); - - /** - * Removes a {@link CompassListener} that can be used to - * receive heading and state changes. - * - * @param compassListener to be removed - */ - void removeCompassListener(@NonNull CompassListener compassListener); - - /** - * Returns the last heading value produced and pushed via - * a compass listener. - * - * @return last heading value - */ - float getLastHeading(); - - /** - * Provides the last know accuracy status from the sensor manager. - *

- * An integer value which is identical to the {@code SensorManager} class constants: - *

    - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}
  • - *
- * - * @return last accuracy status - */ - int getLastAccuracySensorStatus(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java deleted file mode 100644 index c44218a1d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/CompassListener.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -/** - * Callbacks related to the compass - */ -public interface CompassListener { - - /** - * Callback's invoked when a new compass update occurs. You can listen into the compass updates - * using {@link CompassEngine#addCompassListener(CompassListener)} and implementing these - * callbacks. Note that this interface is also used internally to to update the UI chevron/arrow. - * - * @param userHeading the new compass heading - */ - void onCompassChanged(float userHeading); - - /** - * This gets invoked when the compass accuracy status changes from one value to another. It - * provides an integer value which is identical to the {@code SensorManager} class constants: - *
    - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_NO_CONTACT}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_UNRELIABLE}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_LOW}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_MEDIUM}
  • - *
  • {@link android.hardware.SensorManager#SENSOR_STATUS_ACCURACY_HIGH}
  • - *
- * - * @param compassStatus the new accuracy of this sensor, one of - * {@code SensorManager.SENSOR_STATUS_*} - */ - void onCompassAccuracyChange(int compassStatus); -} - diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java deleted file mode 100644 index c631b1b75..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LatLngEvaluator.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.TypeEvaluator; - -import android.support.annotation.NonNull; -import com.mapbox.mapboxsdk.geometry.LatLng; - -class LatLngEvaluator implements TypeEvaluator { - - private final LatLng latLng = new LatLng(); - - @NonNull - @Override - public LatLng evaluate(float fraction, @NonNull LatLng startValue, @NonNull LatLng endValue) { - latLng.setLatitude(startValue.getLatitude() - + ((endValue.getLatitude() - startValue.getLatitude()) * fraction)); - latLng.setLongitude(startValue.getLongitude() - + ((endValue.getLongitude() - startValue.getLongitude()) * fraction)); - return latLng; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java deleted file mode 100644 index 14a1b49dc..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerBitmapProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.support.annotation.ColorInt; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.utils.BitmapUtils; - -import static com.mapbox.mapboxsdk.location.Utils.generateShadow; - -class LayerBitmapProvider { - - private final Context context; - - LayerBitmapProvider(Context context) { - this.context = context; - } - - Bitmap generateBitmap(@DrawableRes int drawableRes, @ColorInt Integer tintColor) { - Drawable drawable = BitmapUtils.getDrawableFromRes(context, drawableRes, tintColor); - return BitmapUtils.getBitmapFromDrawable(drawable); - } - - Bitmap generateShadowBitmap(@NonNull LocationComponentOptions options) { - Drawable shadowDrawable = BitmapUtils.getDrawableFromRes(context, R.drawable.mapbox_user_icon_shadow); - return generateShadow(shadowDrawable, options.elevation()); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java deleted file mode 100644 index faa83ea0e..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerFeatureProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Point; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE; - -class LayerFeatureProvider { - - @NonNull - Feature generateLocationFeature(@Nullable Feature locationFeature, @NonNull LocationComponentOptions options) { - if (locationFeature != null) { - return locationFeature; - } - locationFeature = Feature.fromGeometry(Point.fromLngLat(0.0, 0.0)); - locationFeature.addNumberProperty(PROPERTY_GPS_BEARING, 0f); - locationFeature.addNumberProperty(PROPERTY_COMPASS_BEARING, 0f); - locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, options.enableStaleState()); - return locationFeature; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java deleted file mode 100644 index cac513c2f..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LayerSourceProvider.java +++ /dev/null @@ -1,111 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.support.annotation.NonNull; -import com.mapbox.geojson.Feature; -import com.mapbox.mapboxsdk.style.layers.CircleLayer; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.Property; -import com.mapbox.mapboxsdk.style.layers.SymbolLayer; -import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions; -import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_ALPHA; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_COLOR; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BEARING_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER; -import static com.mapbox.mapboxsdk.style.expressions.Expression.get; -import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; -import static com.mapbox.mapboxsdk.style.expressions.Expression.match; -import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; -import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; -import static com.mapbox.mapboxsdk.style.layers.Property.ICON_ROTATION_ALIGNMENT_MAP; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleColor; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleOpacity; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circlePitchAlignment; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleRadius; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.circleStrokeColor; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconOffset; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotate; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconRotationAlignment; - -class LayerSourceProvider { - - private static final String EMPTY_STRING = ""; - - @NonNull - GeoJsonSource generateSource(Feature locationFeature) { - return new GeoJsonSource( - LOCATION_SOURCE, - locationFeature, - new GeoJsonOptions().withMaxZoom(16) - ); - } - - @NonNull - Layer generateLayer(@NonNull String layerId) { - SymbolLayer layer = new SymbolLayer(layerId, LOCATION_SOURCE); - layer.setProperties( - iconAllowOverlap(true), - iconIgnorePlacement(true), - iconRotationAlignment(ICON_ROTATION_ALIGNMENT_MAP), - iconRotate( - match(literal(layerId), literal(0f), - stop(FOREGROUND_LAYER, get(PROPERTY_GPS_BEARING)), - stop(BACKGROUND_LAYER, get(PROPERTY_GPS_BEARING)), - stop(SHADOW_LAYER, get(PROPERTY_GPS_BEARING)), - stop(BEARING_LAYER, get(PROPERTY_COMPASS_BEARING)) - ) - ), - iconImage( - match(literal(layerId), literal(EMPTY_STRING), - stop(FOREGROUND_LAYER, switchCase( - get(PROPERTY_LOCATION_STALE), get(PROPERTY_FOREGROUND_STALE_ICON), - get(PROPERTY_FOREGROUND_ICON))), - stop(BACKGROUND_LAYER, switchCase( - get(PROPERTY_LOCATION_STALE), get(PROPERTY_BACKGROUND_STALE_ICON), - get(PROPERTY_BACKGROUND_ICON))), - stop(SHADOW_LAYER, literal(SHADOW_ICON)), - stop(BEARING_LAYER, get(PROPERTY_BEARING_ICON)) - ) - ), - iconOffset( - match(literal(layerId), literal(new Float[] {0f, 0f}), - stop(literal(FOREGROUND_LAYER), get(PROPERTY_FOREGROUND_ICON_OFFSET)), - stop(literal(SHADOW_LAYER), get(PROPERTY_SHADOW_ICON_OFFSET)) - ) - ) - ); - return layer; - } - - @NonNull - Layer generateAccuracyLayer() { - return new CircleLayer(ACCURACY_LAYER, LOCATION_SOURCE) - .withProperties( - circleRadius(get(PROPERTY_ACCURACY_RADIUS)), - circleColor(get(PROPERTY_ACCURACY_COLOR)), - circleOpacity(get(PROPERTY_ACCURACY_ALPHA)), - circleStrokeColor(get(PROPERTY_ACCURACY_COLOR)), - circlePitchAlignment(Property.CIRCLE_PITCH_ALIGNMENT_MAP) - ); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java deleted file mode 100644 index 5aad038a2..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinator.java +++ /dev/null @@ -1,422 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.Animator; -import android.location.Location; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.util.SparseArray; -import android.view.animation.LinearInterpolator; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.COMPASS_UPDATE_RATE_MS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.MAX_ANIMATION_DURATION_MS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_LATLNG; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_ACCURACY; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_LATLNG; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_TILT; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM; -import static com.mapbox.mapboxsdk.location.Utils.immediateAnimation; - -final class LocationAnimatorCoordinator { - - private static final String TAG = "Mbgl-LocationAnimatorCoordinator"; - - @VisibleForTesting - final SparseArray animatorArray = new SparseArray<>(); - - private final Projection projection; - private Location previousLocation; - private float previousAccuracyRadius = -1; - private float previousCompassBearing = -1; - private long locationUpdateTimestamp = -1; - private float durationMultiplier; - private final MapboxAnimatorProvider animatorProvider; - private final MapboxAnimatorSetProvider animatorSetProvider; - private boolean compassAnimationEnabled; - private boolean accuracyAnimationEnabled; - - @VisibleForTesting - int maxAnimationFps = Integer.MAX_VALUE; - - @VisibleForTesting - final SparseArray listeners = new SparseArray<>(); - - LocationAnimatorCoordinator(@NonNull Projection projection, @NonNull MapboxAnimatorSetProvider animatorSetProvider, - @NonNull MapboxAnimatorProvider animatorProvider) { - this.projection = projection; - this.animatorProvider = animatorProvider; - this.animatorSetProvider = animatorSetProvider; - } - - void updateAnimatorListenerHolders(@NonNull Set listenerHolders) { - listeners.clear(); - for (AnimatorListenerHolder holder : listenerHolders) { - listeners.append(holder.getAnimatorType(), holder.getListener()); - } - } - - void feedNewLocation(@NonNull Location newLocation, @NonNull CameraPosition currentCameraPosition, - boolean isGpsNorth) { - if (previousLocation == null) { - previousLocation = newLocation; - locationUpdateTimestamp = SystemClock.elapsedRealtime() - TRANSITION_ANIMATION_DURATION_MS; - } - - LatLng previousLayerLatLng = getPreviousLayerLatLng(); - float previousLayerBearing = getPreviousLayerGpsBearing(); - LatLng previousCameraLatLng = currentCameraPosition.target; - float previousCameraBearing = (float) currentCameraPosition.bearing; - - LatLng targetLatLng = new LatLng(newLocation); - float targetLayerBearing = newLocation.getBearing(); - float targetCameraBearing = newLocation.getBearing(); - targetCameraBearing = checkGpsNorth(isGpsNorth, targetCameraBearing); - - updateLayerAnimators(previousLayerLatLng, targetLatLng, previousLayerBearing, targetLayerBearing); - updateCameraAnimators(previousCameraLatLng, previousCameraBearing, targetLatLng, targetCameraBearing); - - boolean snap = immediateAnimation(projection, previousCameraLatLng, targetLatLng) - || immediateAnimation(projection, previousLayerLatLng, targetLatLng); - - long animationDuration = 0; - if (!snap) { - long previousUpdateTimeStamp = locationUpdateTimestamp; - locationUpdateTimestamp = SystemClock.elapsedRealtime(); - - if (previousUpdateTimeStamp == 0) { - animationDuration = 0; - } else { - animationDuration = (long) ((locationUpdateTimestamp - previousUpdateTimeStamp) * durationMultiplier) - /* make animation slightly longer with durationMultiplier, defaults to 1.1f */; - } - - animationDuration = Math.min(animationDuration, MAX_ANIMATION_DURATION_MS); - } - - playAnimators(animationDuration, - ANIMATOR_LAYER_LATLNG, - ANIMATOR_LAYER_GPS_BEARING, - ANIMATOR_CAMERA_LATLNG, - ANIMATOR_CAMERA_GPS_BEARING); - - previousLocation = newLocation; - } - - void feedNewCompassBearing(float targetCompassBearing, @NonNull CameraPosition currentCameraPosition) { - if (previousCompassBearing < 0) { - previousCompassBearing = targetCompassBearing; - } - - float previousLayerBearing = getPreviousLayerCompassBearing(); - float previousCameraBearing = (float) currentCameraPosition.bearing; - - updateCompassAnimators(targetCompassBearing, previousLayerBearing, previousCameraBearing); - playAnimators( - compassAnimationEnabled ? COMPASS_UPDATE_RATE_MS : 0, - ANIMATOR_LAYER_COMPASS_BEARING, - ANIMATOR_CAMERA_COMPASS_BEARING); - - previousCompassBearing = targetCompassBearing; - } - - void feedNewAccuracyRadius(float targetAccuracyRadius, boolean noAnimation) { - if (previousAccuracyRadius < 0) { - previousAccuracyRadius = targetAccuracyRadius; - } - - float previousAccuracyRadius = getPreviousAccuracyRadius(); - updateAccuracyAnimators(targetAccuracyRadius, previousAccuracyRadius); - playAnimators( - noAnimation || !accuracyAnimationEnabled ? 0 : ACCURACY_RADIUS_ANIMATION_DURATION, - ANIMATOR_LAYER_ACCURACY); - - this.previousAccuracyRadius = targetAccuracyRadius; - } - - void feedNewZoomLevel(double targetZoomLevel, @NonNull CameraPosition currentCameraPosition, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - updateZoomAnimator((float) targetZoomLevel, (float) currentCameraPosition.zoom, callback); - playAnimators(animationDuration, ANIMATOR_ZOOM); - } - - void feedNewTilt(double targetTilt, @NonNull CameraPosition currentCameraPosition, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - updateTiltAnimator((float) targetTilt, (float) currentCameraPosition.tilt, callback); - playAnimators(animationDuration, ANIMATOR_TILT); - } - - private LatLng getPreviousLayerLatLng() { - LatLng previousLatLng; - MapboxAnimator latLngAnimator = animatorArray.get(ANIMATOR_LAYER_LATLNG); - if (latLngAnimator != null) { - previousLatLng = (LatLng) latLngAnimator.getAnimatedValue(); - } else { - previousLatLng = new LatLng(previousLocation); - } - return previousLatLng; - } - - private float getPreviousLayerGpsBearing() { - MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_GPS_BEARING); - float previousBearing; - if (animator != null) { - previousBearing = (float) animator.getAnimatedValue(); - } else { - previousBearing = previousLocation.getBearing(); - } - return previousBearing; - } - - private float getPreviousLayerCompassBearing() { - MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING); - - float previousBearing; - if (animator != null) { - previousBearing = (float) animator.getAnimatedValue(); - } else { - previousBearing = previousCompassBearing; - } - return previousBearing; - } - - private float getPreviousAccuracyRadius() { - MapboxAnimator animator = animatorArray.get(ANIMATOR_LAYER_ACCURACY); - float previousRadius; - if (animator != null) { - previousRadius = (float) animator.getAnimatedValue(); - } else { - previousRadius = previousAccuracyRadius; - } - return previousRadius; - } - - private void updateLayerAnimators(LatLng previousLatLng, LatLng targetLatLng, - float previousBearing, float targetBearing) { - createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, previousLatLng, targetLatLng); - - // Because Location bearing values are normalized to [0, 360] - // we need to do the same for the previous bearing value to determine the shortest path - previousBearing = Utils.normalize(previousBearing); - float normalizedLayerBearing = Utils.shortestRotation(targetBearing, previousBearing); - createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, previousBearing, normalizedLayerBearing); - } - - private void updateCameraAnimators(LatLng previousCameraLatLng, float previousCameraBearing, - LatLng targetLatLng, float targetBearing) { - createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraLatLng, targetLatLng); - - float normalizedCameraBearing = Utils.shortestRotation(targetBearing, previousCameraBearing); - createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing); - } - - private void updateCompassAnimators(float targetCompassBearing, float previousLayerBearing, - float previousCameraBearing) { - float normalizedLayerBearing = Utils.shortestRotation(targetCompassBearing, previousLayerBearing); - createNewFloatAnimator(ANIMATOR_LAYER_COMPASS_BEARING, previousLayerBearing, normalizedLayerBearing); - - float normalizedCameraBearing = Utils.shortestRotation(targetCompassBearing, previousCameraBearing); - createNewFloatAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, previousCameraBearing, normalizedCameraBearing); - } - - private void updateAccuracyAnimators(float targetAccuracyRadius, float previousAccuracyRadius) { - createNewFloatAnimator(ANIMATOR_LAYER_ACCURACY, previousAccuracyRadius, targetAccuracyRadius); - } - - private void updateZoomAnimator(float targetZoomLevel, float previousZoomLevel, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - createNewCameraAdapterAnimator(ANIMATOR_ZOOM, previousZoomLevel, targetZoomLevel, cancelableCallback); - } - - private void updateTiltAnimator(float targetTilt, float previousTiltLevel, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - createNewCameraAdapterAnimator(ANIMATOR_TILT, previousTiltLevel, targetTilt, cancelableCallback); - } - - private void createNewLatLngAnimator(@MapboxAnimator.Type int animatorType, LatLng previous, LatLng target) { - cancelAnimator(animatorType); - MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType); - if (listener != null) { - animatorArray.put(animatorType, animatorProvider.latLngAnimator(previous, target, listener, maxAnimationFps)); - } - } - - private void createNewFloatAnimator(@MapboxAnimator.Type int animatorType, float previous, float target) { - cancelAnimator(animatorType); - MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType); - if (listener != null) { - animatorArray.put(animatorType, animatorProvider.floatAnimator(previous, target, listener, maxAnimationFps)); - } - } - - private void createNewCameraAdapterAnimator(@MapboxAnimator.Type int animatorType, float previous, float target, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - cancelAnimator(animatorType); - MapboxAnimator.AnimationsValueChangeListener listener = listeners.get(animatorType); - if (listener != null) { - animatorArray.put(animatorType, animatorProvider.cameraAnimator(previous, target, listener, cancelableCallback)); - } - } - - private float checkGpsNorth(boolean isGpsNorth, float targetCameraBearing) { - if (isGpsNorth) { - targetCameraBearing = 0; - } - return targetCameraBearing; - } - - private void playAnimators(long duration, @MapboxAnimator.Type int... animatorTypes) { - List animators = new ArrayList<>(); - for (@MapboxAnimator.Type int animatorType : animatorTypes) { - Animator animator = animatorArray.get(animatorType); - if (animator != null) { - animators.add(animator); - } - } - animatorSetProvider.startAnimation(animators, new LinearInterpolator(), duration); - } - - void resetAllCameraAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) { - resetCameraCompassAnimation(currentCameraPosition); - boolean snap = resetCameraLocationAnimations(currentCameraPosition, isGpsNorth); - playAnimators( - snap ? 0 : TRANSITION_ANIMATION_DURATION_MS, - ANIMATOR_CAMERA_LATLNG, - ANIMATOR_CAMERA_GPS_BEARING); - } - - private boolean resetCameraLocationAnimations(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) { - resetCameraGpsBearingAnimation(currentCameraPosition, isGpsNorth); - return resetCameraLatLngAnimation(currentCameraPosition); - } - - private boolean resetCameraLatLngAnimation(@NonNull CameraPosition currentCameraPosition) { - MapboxLatLngAnimator animator = (MapboxLatLngAnimator) animatorArray.get(ANIMATOR_CAMERA_LATLNG); - if (animator == null) { - return false; - } - - LatLng currentTarget = animator.getTarget(); - LatLng previousCameraTarget = currentCameraPosition.target; - createNewLatLngAnimator(ANIMATOR_CAMERA_LATLNG, previousCameraTarget, currentTarget); - - return immediateAnimation(projection, previousCameraTarget, currentTarget); - } - - private void resetCameraGpsBearingAnimation(@NonNull CameraPosition currentCameraPosition, boolean isGpsNorth) { - MapboxFloatAnimator animator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_CAMERA_GPS_BEARING); - if (animator == null) { - return; - } - - float currentTargetBearing = animator.getTarget(); - currentTargetBearing = checkGpsNorth(isGpsNorth, currentTargetBearing); - float previousCameraBearing = (float) currentCameraPosition.bearing; - float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); - createNewFloatAnimator(ANIMATOR_CAMERA_GPS_BEARING, previousCameraBearing, normalizedCameraBearing); - } - - private void resetCameraCompassAnimation(@NonNull CameraPosition currentCameraPosition) { - MapboxFloatAnimator animator = - (MapboxFloatAnimator) animatorArray.get(ANIMATOR_CAMERA_COMPASS_BEARING); - if (animator == null) { - return; - } - - float currentTargetBearing = animator.getTarget(); - float previousCameraBearing = (float) currentCameraPosition.bearing; - float normalizedCameraBearing = Utils.shortestRotation(currentTargetBearing, previousCameraBearing); - createNewFloatAnimator(ANIMATOR_CAMERA_COMPASS_BEARING, previousCameraBearing, normalizedCameraBearing); - } - - void resetAllLayerAnimations() { - MapboxLatLngAnimator latLngAnimator = (MapboxLatLngAnimator) animatorArray.get(ANIMATOR_LAYER_LATLNG); - MapboxFloatAnimator gpsBearingAnimator = (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_GPS_BEARING); - MapboxFloatAnimator compassBearingAnimator = - (MapboxFloatAnimator) animatorArray.get(ANIMATOR_LAYER_COMPASS_BEARING); - - if (latLngAnimator != null && gpsBearingAnimator != null) { - LatLng currentLatLng = (LatLng) latLngAnimator.getAnimatedValue(); - LatLng currentLatLngTarget = latLngAnimator.getTarget(); - createNewLatLngAnimator(ANIMATOR_LAYER_LATLNG, currentLatLng, currentLatLngTarget); - - float currentGpsBearing = (float) gpsBearingAnimator.getAnimatedValue(); - float currentGpsBearingTarget = gpsBearingAnimator.getTarget(); - createNewFloatAnimator(ANIMATOR_LAYER_GPS_BEARING, currentGpsBearing, currentGpsBearingTarget); - - long duration = latLngAnimator.getDuration() - latLngAnimator.getCurrentPlayTime(); - - playAnimators(duration, ANIMATOR_LAYER_LATLNG, ANIMATOR_LAYER_GPS_BEARING); - } - - if (compassBearingAnimator != null) { - float currentLayerBearing = getPreviousLayerCompassBearing(); - float currentLayerBearingTarget = compassBearingAnimator.getTarget(); - createNewFloatAnimator(ANIMATOR_LAYER_COMPASS_BEARING, currentLayerBearing, currentLayerBearingTarget); - playAnimators( - compassAnimationEnabled ? COMPASS_UPDATE_RATE_MS : 0, - ANIMATOR_LAYER_COMPASS_BEARING); - } - } - - void cancelZoomAnimation() { - cancelAnimator(ANIMATOR_ZOOM); - } - - void cancelTiltAnimation() { - cancelAnimator(ANIMATOR_TILT); - } - - void cancelAllAnimations() { - for (int i = 0; i < animatorArray.size(); i++) { - @MapboxAnimator.Type int animatorType = animatorArray.keyAt(i); - cancelAnimator(animatorType); - } - } - - private void cancelAnimator(@MapboxAnimator.Type int animatorType) { - MapboxAnimator animator = animatorArray.get(animatorType); - if (animator != null) { - animator.cancel(); - animator.removeAllUpdateListeners(); - animator.removeAllListeners(); - animatorArray.put(animatorType, null); - } - } - - void setTrackingAnimationDurationMultiplier(float trackingAnimationDurationMultiplier) { - this.durationMultiplier = trackingAnimationDurationMultiplier; - } - - void setCompassAnimationEnabled(boolean compassAnimationEnabled) { - this.compassAnimationEnabled = compassAnimationEnabled; - } - - void setAccuracyAnimationEnabled(boolean accuracyAnimationEnabled) { - this.accuracyAnimationEnabled = accuracyAnimationEnabled; - } - - void setMaxAnimationFps(int maxAnimationFps) { - if (maxAnimationFps <= 0) { - Logger.e(TAG, "Max animation FPS cannot be less or equal to 0."); - return; - } - this.maxAnimationFps = maxAnimationFps; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java deleted file mode 100644 index 79d2e02bf..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationCameraController.java +++ /dev/null @@ -1,437 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.graphics.PointF; -import android.location.Location; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.view.MotionEvent; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.android.gestures.RotateGestureDetector; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.location.modes.CameraMode; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Transform; - -import java.util.HashSet; -import java.util.Set; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; - -final class LocationCameraController { - - @CameraMode.Mode - private int cameraMode; - - private final MapboxMap mapboxMap; - private final Transform transform; - private final OnCameraTrackingChangedListener internalCameraTrackingChangedListener; - private LocationComponentOptions options; - private boolean adjustFocalPoint; - - private final MoveGestureDetector moveGestureDetector; - private final OnCameraMoveInvalidateListener onCameraMoveInvalidateListener; - - private final AndroidGesturesManager initialGesturesManager; - private final AndroidGesturesManager internalGesturesManager; - - private boolean isTransitioning; - - LocationCameraController( - Context context, - MapboxMap mapboxMap, - Transform transform, - OnCameraTrackingChangedListener internalCameraTrackingChangedListener, - @NonNull LocationComponentOptions options, - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener) { - this.mapboxMap = mapboxMap; - this.transform = transform; - - initialGesturesManager = mapboxMap.getGesturesManager(); - internalGesturesManager = new LocationGesturesManager(context); - moveGestureDetector = internalGesturesManager.getMoveGestureDetector(); - mapboxMap.addOnRotateListener(onRotateListener); - mapboxMap.addOnFlingListener(onFlingListener); - mapboxMap.addOnMoveListener(onMoveListener); - - this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener; - this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener; - initializeOptions(options); - } - - // Package private for testing purposes - LocationCameraController(MapboxMap mapboxMap, - Transform transform, - MoveGestureDetector moveGestureDetector, - OnCameraTrackingChangedListener internalCameraTrackingChangedListener, - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener, - AndroidGesturesManager initialGesturesManager, - AndroidGesturesManager internalGesturesManager) { - this.mapboxMap = mapboxMap; - this.transform = transform; - this.moveGestureDetector = moveGestureDetector; - this.internalCameraTrackingChangedListener = internalCameraTrackingChangedListener; - this.onCameraMoveInvalidateListener = onCameraMoveInvalidateListener; - this.internalGesturesManager = internalGesturesManager; - this.initialGesturesManager = initialGesturesManager; - } - - void initializeOptions(LocationComponentOptions options) { - this.options = options; - if (options.trackingGesturesManagement()) { - if (mapboxMap.getGesturesManager() != internalGesturesManager) { - mapboxMap.setGesturesManager(internalGesturesManager, true, true); - } - adjustGesturesThresholds(); - } else if (mapboxMap.getGesturesManager() != initialGesturesManager) { - mapboxMap.setGesturesManager(initialGesturesManager, true, true); - } - } - - void setCameraMode(@CameraMode.Mode int cameraMode) { - setCameraMode(cameraMode, null, TRANSITION_ANIMATION_DURATION_MS, null, null, null, null); - } - - void setCameraMode(@CameraMode.Mode final int cameraMode, @Nullable Location lastLocation, - long transitionDuration, - @Nullable Double zoom, @Nullable Double bearing, @Nullable Double tilt, - @Nullable OnLocationCameraTransitionListener internalTransitionListener) { - if (this.cameraMode == cameraMode) { - return; - } - - final boolean wasTracking = isLocationTracking(); - this.cameraMode = cameraMode; - - if (cameraMode != CameraMode.NONE) { - mapboxMap.cancelTransitions(); - } - - adjustGesturesThresholds(); - notifyCameraTrackingChangeListener(wasTracking); - transitionToCurrentLocation( - wasTracking, lastLocation, transitionDuration, zoom, bearing, tilt, internalTransitionListener); - } - - /** - * Initiates a camera animation to the current location if location tracking was engaged. - * Notifies an internal listener when the transition's finished to invalidate animators and notify external listeners. - */ - private void transitionToCurrentLocation(boolean wasTracking, Location lastLocation, - long transitionDuration, - Double zoom, Double bearing, Double tilt, - final OnLocationCameraTransitionListener internalTransitionListener) { - if (!wasTracking && isLocationTracking() && lastLocation != null) { - isTransitioning = true; - LatLng target = new LatLng(lastLocation); - - CameraPosition.Builder builder = new CameraPosition.Builder().target(target); - if (zoom != null) { - builder.zoom(zoom); - } - if (tilt != null) { - builder.tilt(tilt); - } - if (bearing != null) { - builder.bearing(bearing); - } else { - if (isLocationBearingTracking()) { - builder.bearing(cameraMode == CameraMode.TRACKING_GPS_NORTH ? 0 : lastLocation.getBearing()); - } - } - - CameraUpdate update = CameraUpdateFactory.newCameraPosition(builder.build()); - MapboxMap.CancelableCallback callback = new MapboxMap.CancelableCallback() { - @Override - public void onCancel() { - isTransitioning = false; - if (internalTransitionListener != null) { - internalTransitionListener.onLocationCameraTransitionCanceled(cameraMode); - } - } - - @Override - public void onFinish() { - isTransitioning = false; - if (internalTransitionListener != null) { - internalTransitionListener.onLocationCameraTransitionFinished(cameraMode); - } - } - }; - - CameraPosition currentPosition = mapboxMap.getCameraPosition(); - if (Utils.immediateAnimation(mapboxMap.getProjection(), currentPosition.target, target)) { - transform.moveCamera( - mapboxMap, - update, - callback); - } else { - transform.animateCamera( - mapboxMap, - update, - (int) transitionDuration, - callback); - } - } else { - if (internalTransitionListener != null) { - internalTransitionListener.onLocationCameraTransitionFinished(cameraMode); - } - } - } - - int getCameraMode() { - return cameraMode; - } - - private void setBearing(float bearing) { - if (isTransitioning) { - return; - } - - transform.moveCamera(mapboxMap, CameraUpdateFactory.bearingTo(bearing), null); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - private void setLatLng(@NonNull LatLng latLng) { - if (isTransitioning) { - return; - } - - transform.moveCamera(mapboxMap, CameraUpdateFactory.newLatLng(latLng), null); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - - if (adjustFocalPoint) { - PointF focalPoint = mapboxMap.getProjection().toScreenLocation(latLng); - mapboxMap.getUiSettings().setFocalPoint(focalPoint); - adjustFocalPoint = false; - } - } - - private void setZoom(float zoom) { - if (isTransitioning) { - return; - } - - transform.moveCamera(mapboxMap, CameraUpdateFactory.zoomTo(zoom), null); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - private void setTilt(float tilt) { - if (isTransitioning) { - return; - } - - transform.moveCamera(mapboxMap, CameraUpdateFactory.tiltTo(tilt), null); - onCameraMoveInvalidateListener.onInvalidateCameraMove(); - } - - private final MapboxAnimator.AnimationsValueChangeListener latLngValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(LatLng value) { - setLatLng(value); - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener gpsBearingValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - boolean trackingNorth = cameraMode == CameraMode.TRACKING_GPS_NORTH - && mapboxMap.getCameraPosition().bearing == 0; - - if (!trackingNorth) { - setBearing(value); - } - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener compassBearingValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - if (cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_COMPASS) { - setBearing(value); - } - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener zoomValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - setZoom(value); - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener tiltValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - setTilt(value); - } - }; - - Set getAnimationListeners() { - Set holders = new HashSet<>(); - if (isLocationTracking()) { - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_CAMERA_LATLNG, latLngValueListener)); - } - - if (isLocationBearingTracking()) { - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING, gpsBearingValueListener)); - } - - if (isConsumingCompass()) { - holders.add(new AnimatorListenerHolder( - MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING, - compassBearingValueListener)); - } - - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_ZOOM, zoomValueListener)); - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_TILT, tiltValueListener)); - return holders; - } - - boolean isTransitioning() { - return isTransitioning; - } - - private void adjustGesturesThresholds() { - if (options.trackingGesturesManagement()) { - if (isLocationTracking()) { - adjustFocalPoint = true; - moveGestureDetector.setMoveThreshold(options.trackingInitialMoveThreshold()); - } else { - moveGestureDetector.setMoveThreshold(0f); - } - } - } - - boolean isConsumingCompass() { - return cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_COMPASS; - } - - private boolean isLocationTracking() { - return cameraMode == CameraMode.TRACKING - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH; - } - - private boolean isBearingTracking() { - return cameraMode == CameraMode.NONE_COMPASS - || cameraMode == CameraMode.TRACKING_COMPASS - || cameraMode == CameraMode.NONE_GPS - || cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH; - } - - private boolean isLocationBearingTracking() { - return cameraMode == CameraMode.TRACKING_GPS - || cameraMode == CameraMode.TRACKING_GPS_NORTH - || cameraMode == CameraMode.NONE_GPS; - } - - private void notifyCameraTrackingChangeListener(boolean wasTracking) { - internalCameraTrackingChangedListener.onCameraTrackingChanged(cameraMode); - if (wasTracking && !isLocationTracking()) { - mapboxMap.getUiSettings().setFocalPoint(null); - internalCameraTrackingChangedListener.onCameraTrackingDismissed(); - } - } - - @NonNull - @VisibleForTesting - MapboxMap.OnMoveListener onMoveListener = new MapboxMap.OnMoveListener() { - private boolean interrupt; - - @Override - public void onMoveBegin(@NonNull MoveGestureDetector detector) { - if (options.trackingGesturesManagement() - && detector.getPointersCount() > 1 - && detector.getMoveThreshold() != options.trackingMultiFingerMoveThreshold() - && isLocationTracking()) { - detector.setMoveThreshold(options.trackingMultiFingerMoveThreshold()); - interrupt = true; - } else { - setCameraMode(CameraMode.NONE); - } - } - - @Override - public void onMove(@NonNull MoveGestureDetector detector) { - if (interrupt) { - detector.interrupt(); - return; - } - - if (isLocationTracking() || isBearingTracking()) { - setCameraMode(CameraMode.NONE); - detector.interrupt(); - } - } - - @Override - public void onMoveEnd(@NonNull MoveGestureDetector detector) { - if (options.trackingGesturesManagement() && !interrupt && isLocationTracking()) { - detector.setMoveThreshold(options.trackingInitialMoveThreshold()); - } - interrupt = false; - } - }; - - @NonNull - private MapboxMap.OnRotateListener onRotateListener = new MapboxMap.OnRotateListener() { - @Override - public void onRotateBegin(@NonNull RotateGestureDetector detector) { - if (isBearingTracking()) { - setCameraMode(CameraMode.NONE); - } - } - - @Override - public void onRotate(@NonNull RotateGestureDetector detector) { - // no implementation - } - - @Override - public void onRotateEnd(@NonNull RotateGestureDetector detector) { - // no implementation - } - }; - - @NonNull - private MapboxMap.OnFlingListener onFlingListener = new MapboxMap.OnFlingListener() { - @Override - public void onFling() { - setCameraMode(CameraMode.NONE); - } - }; - - private class LocationGesturesManager extends AndroidGesturesManager { - - LocationGesturesManager(Context context) { - super(context); - } - - @Override - public boolean onTouchEvent(@Nullable MotionEvent motionEvent) { - if (motionEvent != null) { - int action = motionEvent.getActionMasked(); - if (action == MotionEvent.ACTION_UP) { - adjustGesturesThresholds(); - } - } - return super.onTouchEvent(motionEvent); - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java deleted file mode 100644 index 8b014b0e9..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponent.java +++ /dev/null @@ -1,1618 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.hardware.SensorManager; -import android.location.Location; -import android.os.Looper; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.RequiresPermission; -import android.support.annotation.StyleRes; -import android.support.annotation.VisibleForTesting; -import android.view.WindowManager; - -import com.mapbox.android.core.location.LocationEngine; -import com.mapbox.android.core.location.LocationEngineCallback; -import com.mapbox.android.core.location.LocationEngineProvider; -import com.mapbox.android.core.location.LocationEngineRequest; -import com.mapbox.android.core.location.LocationEngineResult; -import com.mapbox.android.core.permissions.PermissionsManager; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.location.modes.CameraMode; -import com.mapbox.mapboxsdk.location.modes.RenderMode; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; -import com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; -import com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener; -import com.mapbox.mapboxsdk.maps.Style; -import com.mapbox.mapboxsdk.maps.Transform; - -import java.lang.ref.WeakReference; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; - -import static android.Manifest.permission.ACCESS_COARSE_LOCATION; -import static android.Manifest.permission.ACCESS_FINE_LOCATION; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_FASTEST_INTERVAL_MILLIS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_INTERVAL_MILLIS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; - -/** - * The Location Component provides location awareness to your mobile application. Enabling this - * component provides a contextual experience to your users by showing an icon representing the users - * current location. A few different modes are offered to provide the right context to your users at - * the correct time. {@link RenderMode#NORMAL} simply shows the users location on the map - * represented as a dot. {@link RenderMode#COMPASS} mode allows you to display an arrow icon - * (by default) that points in the direction the device is pointing in. - * {@link RenderMode#GPS} can be used in conjunction with our Navigation SDK to - * display a larger icon (customized with {@link LocationComponentOptions#gpsDrawable()}) we call the user puck. - *

- * This component also offers the ability to set a map camera behavior for tracking the user - * location. These different {@link CameraMode}s will track, stop tracking the location based on the - * mode set with {@link LocationComponent#setCameraMode(int)}. - *

- * - * To get the component object use {@link MapboxMap#getLocationComponent()} and activate it with - * {@link #activateLocationComponent(Context, Style)} or one of the overloads. - * Then, manage its visibility with {@link #setLocationComponentEnabled(boolean)}. - * The component will not process location updates right after activation, but only after being enabled. - * - *

- * Using this component requires you to request permission beforehand manually or using - * {@link PermissionsManager}. Either - * {@code ACCESS_COARSE_LOCATION} or {@code ACCESS_FINE_LOCATION} permissions can be requested for - * this component to work as expected. - *

- * This component offers a default, built-in {@link LocationEngine} with some of the activation methods. - * This engine will be obtained by {@link LocationEngineProvider#getBestLocationEngine(Context, boolean)} which defaults - * to the {@link com.mapbox.android.core.location.MapboxFusedLocationEngineImpl}. If you'd like to utilize Google - * Play Services - * for more precise location updates, simply add the Google Play Location Services dependency in your build script. - * This will make the default engine the {@link com.mapbox.android.core.location.GoogleLocationEngineImpl} instead. - * After a custom engine is passed to the component, or the built-in is initialized, - * the location updates are going to be requested with the {@link LocationEngineRequest}, either a default one, - * or the one passed during the activation. - * When using any engine, requesting/removing the location updates is going to be managed internally. - *

- * You can also push location updates to the component without any internal engine management. - * To achieve that, use {@link #activateLocationComponent(Context, Style, boolean)} with false. - * No engine is going to be initialized and you can push location updates with {@link #forceLocationUpdate(Location)}. - *

- * For location puck animation purposes, like navigation, - * we recommend limiting the maximum zoom level of the map for the best user experience. - *

- * Location Component doesn't support state saving out-of-the-box. - */ -public final class LocationComponent { - private static final String TAG = "Mbgl-LocationComponent"; - - @NonNull - private final MapboxMap mapboxMap; - @NonNull - private final Transform transform; - private Style style; - private LocationComponentOptions options; - @NonNull - private InternalLocationEngineProvider internalLocationEngineProvider = new InternalLocationEngineProvider(); - @Nullable - private LocationEngine locationEngine; - @NonNull - private LocationEngineRequest locationEngineRequest = - new LocationEngineRequest.Builder(DEFAULT_INTERVAL_MILLIS) - .setFastestInterval(DEFAULT_FASTEST_INTERVAL_MILLIS) - .setPriority(LocationEngineRequest.PRIORITY_HIGH_ACCURACY) - .build(); - private LocationEngineCallback currentLocationEngineListener - = new CurrentLocationEngineCallback(this); - private LocationEngineCallback lastLocationEngineListener - = new LastLocationEngineCallback(this); - - @Nullable - private CompassEngine compassEngine; - - private LocationLayerController locationLayerController; - private LocationCameraController locationCameraController; - - private LocationAnimatorCoordinator locationAnimatorCoordinator; - - /** - * Holds last location which is being returned in the {@link #getLastKnownLocation()} - * when there is no {@link #locationEngine} set or when the last location returned by the engine is null. - */ - @Nullable - private Location lastLocation; - private CameraPosition lastCameraPosition; - - /** - * Indicates whether the component has been initialized. - */ - private boolean isComponentInitialized; - - /** - * Indicates that the component is enabled and should be displaying location if Mapbox components are available and - * the lifecycle is in a started state. - */ - private boolean isEnabled; - - /** - * Indicated that component's lifecycle {@link #onStart()} method has been called. - * This allows Mapbox components enter started state and display data, and adds state safety for methods like - * {@link #setLocationComponentEnabled(boolean)} - */ - private boolean isComponentStarted; - - /** - * Indicates if Mapbox components are ready to be interacted with. This can differ from {@link #isComponentStarted} - * if the Mapbox style is being reloaded. - */ - private boolean isLayerReady; - - /** - * Indicates whether we are listening for compass updates. - */ - private boolean isListeningToCompass; - - private StaleStateManager staleStateManager; - private final CopyOnWriteArrayList onLocationStaleListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onLocationClickListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onLocationLongClickListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onCameraTrackingChangedListeners - = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onRenderModeChangedListeners - = new CopyOnWriteArrayList<>(); - - // Workaround for too frequent updates, see https://github.com/mapbox/mapbox-gl-native/issues/13587 - private long fastestInterval; - private long lastUpdateTime; - - /** - * Internal use. - *

- * To get the component object use {@link MapboxMap#getLocationComponent()}. - */ - public LocationComponent(@NonNull MapboxMap mapboxMap, - @NonNull Transform transform, - @NonNull List developerAnimationListeners) { - this.mapboxMap = mapboxMap; - this.transform = transform; - developerAnimationListeners.add(developerAnimationListener); - } - - // used for creating a spy - LocationComponent() { - //noinspection ConstantConditions - mapboxMap = null; - transform = null; - } - - @VisibleForTesting - LocationComponent(@NonNull MapboxMap mapboxMap, - @NonNull Transform transform, - @NonNull List developerAnimationListeners, - @NonNull LocationEngineCallback currentListener, - @NonNull LocationEngineCallback lastListener, - @NonNull LocationLayerController locationLayerController, - @NonNull LocationCameraController locationCameraController, - @NonNull LocationAnimatorCoordinator locationAnimatorCoordinator, - @NonNull StaleStateManager staleStateManager, - @NonNull CompassEngine compassEngine, - @NonNull InternalLocationEngineProvider internalLocationEngineProvider) { - this.mapboxMap = mapboxMap; - this.transform = transform; - developerAnimationListeners.add(developerAnimationListener); - this.currentLocationEngineListener = currentListener; - this.lastLocationEngineListener = lastListener; - this.locationLayerController = locationLayerController; - this.locationCameraController = locationCameraController; - this.locationAnimatorCoordinator = locationAnimatorCoordinator; - this.staleStateManager = staleStateManager; - this.compassEngine = compassEngine; - this.internalLocationEngineProvider = internalLocationEngineProvider; - isComponentInitialized = true; - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - *

- * Note: This method will initialize and use an internal {@link LocationEngine} when enabled. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style) { - activateLocationComponent(context, style, - LocationComponentOptions.createFromAttributes(context, R.style.mapbox_LocationComponent)); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if - * there should be no location engine initialized - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - boolean useDefaultLocationEngine) { - if (useDefaultLocationEngine) { - activateLocationComponent(context, style, R.style.mapbox_LocationComponent); - } else { - activateLocationComponent(context, style, null, R.style.mapbox_LocationComponent); - } - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if - * there should be no location engine initialized - * @param locationEngineRequest the location request - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - boolean useDefaultLocationEngine, - @NonNull LocationEngineRequest locationEngineRequest) { - setLocationEngineRequest(locationEngineRequest); - if (useDefaultLocationEngine) { - activateLocationComponent(context, style, R.style.mapbox_LocationComponent); - } else { - activateLocationComponent(context, style, null, R.style.mapbox_LocationComponent); - } - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param useDefaultLocationEngine true if you want to initialize and use the built-in location engine or false if - * there should be no location engine initialized - * @param locationEngineRequest the location request - * @param options the options - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - boolean useDefaultLocationEngine, - @NonNull LocationEngineRequest locationEngineRequest, - @NonNull LocationComponentOptions options) { - setLocationEngineRequest(locationEngineRequest); - if (useDefaultLocationEngine) { - activateLocationComponent(context, style, options); - } else { - activateLocationComponent(context, style, null, options); - } - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - *

- * Note: This method will initialize and use an internal {@link LocationEngine} when enabled. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param styleRes the LocationComponent style res - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, @StyleRes int styleRes) { - activateLocationComponent(context, style, LocationComponentOptions.createFromAttributes(context, styleRes)); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - *

- * Note: This method will initialize and use an internal {@link LocationEngine} when enabled. - *

- * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param options the options - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @NonNull LocationComponentOptions options) { - initialize(context, style, options); - initializeLocationEngine(context); - applyStyle(options); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param locationEngine the engine, or null if you'd like to only force location updates - * @param styleRes the LocationComponent style res - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine, @StyleRes int styleRes) { - activateLocationComponent(context, style, locationEngine, - LocationComponentOptions.createFromAttributes(context, styleRes)); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param locationEngine the engine, or null if you'd like to only force location updates - * @param locationEngineRequest the location request - * @param styleRes the LocationComponent style res - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine, - @NonNull LocationEngineRequest locationEngineRequest, @StyleRes int styleRes) { - activateLocationComponent(context, style, locationEngine, locationEngineRequest, - LocationComponentOptions.createFromAttributes(context, styleRes)); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param locationEngine the engine - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine) { - activateLocationComponent(context, style, locationEngine, R.style.mapbox_LocationComponent); - } - - /** - * This method will show the location icon and enable the camera tracking the location. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param locationEngine the engine - * @param locationEngineRequest the location request - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine, - @NonNull LocationEngineRequest locationEngineRequest) { - activateLocationComponent(context, style, locationEngine, locationEngineRequest, R.style.mapbox_LocationComponent); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param locationEngine the engine, or null if you'd like to only force location updates - * @param style the proxy object for current map style. More info at {@link Style} - * @param options the options - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine, - @NonNull LocationComponentOptions options) { - initialize(context, style, options); - setLocationEngine(locationEngine); - applyStyle(options); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param context the context - * @param style the proxy object for current map style. More info at {@link Style} - * @param locationEngine the engine, or null if you'd like to only force location updates - * @param locationEngineRequest the location request - * @param options the options - * @deprecated use {@link LocationComponentActivationOptions.Builder} instead - */ - @Deprecated - public void activateLocationComponent(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine, - @NonNull LocationEngineRequest locationEngineRequest, - @NonNull LocationComponentOptions options) { - initialize(context, style, options); - setLocationEngineRequest(locationEngineRequest); - setLocationEngine(locationEngine); - applyStyle(options); - } - - /** - * This method initializes the component and needs to be called before any other operations are performed. - * Afterwards, you can manage component's visibility by {@link #setLocationComponentEnabled(boolean)}. - * - * @param activationOptions a fully built {@link LocationComponentActivationOptions} object - */ - public void activateLocationComponent(@NonNull LocationComponentActivationOptions activationOptions) { - LocationComponentOptions options = activationOptions.locationComponentOptions(); - if (options == null) { - int styleRes = activationOptions.styleRes(); - if (styleRes == 0) { - styleRes = R.style.mapbox_LocationComponent; - } - options = LocationComponentOptions.createFromAttributes(activationOptions.context(), styleRes); - } - - // Initialize the LocationComponent with Context, the map's `Style`, and either custom LocationComponentOptions - // or backup options created from default/custom attributes - initialize(activationOptions.context(), activationOptions.style(), options); - - // Apply the LocationComponent styling - // TODO avoid doubling style initialization - applyStyle(options); - - // Set the LocationEngine request if one was given to LocationComponentActivationOptions - LocationEngineRequest locationEngineRequest = activationOptions.locationEngineRequest(); - if (locationEngineRequest != null) { - setLocationEngineRequest(locationEngineRequest); - } - - // Set the LocationEngine if one was given to LocationComponentActivationOptions - LocationEngine locationEngine = activationOptions.locationEngine(); - if (locationEngine != null) { - setLocationEngine(locationEngine); - } else { - if (activationOptions.useDefaultLocationEngine()) { - initializeLocationEngine(activationOptions.context()); - } else { - setLocationEngine(null); - } - } - } - - /** - * Manage component's visibility after activation. - * - * @param isEnabled true if the plugin should be visible and listen for location updates, false otherwise. - */ - @RequiresPermission(anyOf = {ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}) - public void setLocationComponentEnabled(boolean isEnabled) { - checkActivationState(); - if (isEnabled) { - enableLocationComponent(); - } else { - disableLocationComponent(); - } - } - - /** - * Returns whether the plugin is enabled, meaning that location can be displayed and camera modes can be used. - * - * @return true if the plugin is enabled, false otherwise - */ - public boolean isLocationComponentEnabled() { - checkActivationState(); - return isEnabled; - } - - /** - * Sets the camera mode, which determines how the map camera will track the rendered location. - *

- * When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or - * {@link #tiltWhileTracking(double)}. - * Use {@link OnLocationCameraTransitionListener} to listen for the transition state. - *

- *

    - *
  • {@link CameraMode#NONE}: No camera tracking
  • - *
  • {@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing
  • - *
  • {@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing
  • - *
  • {@link CameraMode#TRACKING}: Camera tracks the user location
  • - *
  • {@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass
  • - *
  • {@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing
  • - *
  • {@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north
  • - *
- * - * @param cameraMode one of the modes found in {@link CameraMode} - */ - public void setCameraMode(@CameraMode.Mode int cameraMode) { - setCameraMode(cameraMode, null); - } - - /** - * Sets the camera mode, which determines how the map camera will track the rendered location. - *

- * When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or - * {@link #tiltWhileTracking(double)}. - * Use {@link OnLocationCameraTransitionListener} to listen for the transition state. - *

- *

    - *
  • {@link CameraMode#NONE}: No camera tracking
  • - *
  • {@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing
  • - *
  • {@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing
  • - *
  • {@link CameraMode#TRACKING}: Camera tracks the user location
  • - *
  • {@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass
  • - *
  • {@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing
  • - *
  • {@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north
  • - *
- * - * @param cameraMode one of the modes found in {@link CameraMode} - * @param transitionListener callback that's going to be invoked when the transition animation finishes - */ - public void setCameraMode(@CameraMode.Mode int cameraMode, - @Nullable OnLocationCameraTransitionListener transitionListener) { - setCameraMode(cameraMode, TRANSITION_ANIMATION_DURATION_MS, null, null, null, transitionListener); - } - - /** - * Sets the camera mode, which determines how the map camera will track the rendered location. - *

- * When camera is transitioning to a new mode, it will reject inputs like {@link #zoomWhileTracking(double)} or - * {@link #tiltWhileTracking(double)}. - * Use {@link OnLocationCameraTransitionListener} to listen for the transition state. - *

- * Set values of zoom, bearing and tilt that the camera will transition to. If null is passed to any of those, - * current value will be used for that parameter instead. - * If the camera is already tracking, provided values are ignored. - *

- *

    - *
  • {@link CameraMode#NONE}: No camera tracking
  • - *
  • {@link CameraMode#NONE_COMPASS}: Camera does not track location, but does track compass bearing
  • - *
  • {@link CameraMode#NONE_GPS}: Camera does not track location, but does track GPS bearing
  • - *
  • {@link CameraMode#TRACKING}: Camera tracks the user location
  • - *
  • {@link CameraMode#TRACKING_COMPASS}: Camera tracks the user location, with bearing provided by a compass
  • - *
  • {@link CameraMode#TRACKING_GPS}: Camera tracks the user location, with normalized bearing
  • - *
  • {@link CameraMode#TRACKING_GPS_NORTH}: Camera tracks the user location, with bearing always set to north
  • - *
- * - * @param cameraMode one of the modes found in {@link CameraMode} - * @param transitionDuration duration of the transition in milliseconds - * @param zoom target zoom, set to null to use current camera position - * @param bearing target bearing, set to null to use current camera position - * @param tilt target tilt, set to null to use current camera position - * @param transitionListener callback that's going to be invoked when the transition animation finishes - */ - public void setCameraMode(@CameraMode.Mode int cameraMode, - long transitionDuration, - @Nullable Double zoom, @Nullable Double bearing, @Nullable Double tilt, - @Nullable OnLocationCameraTransitionListener transitionListener) { - checkActivationState(); - locationCameraController.setCameraMode( - cameraMode, lastLocation, transitionDuration, zoom, bearing, tilt, - new CameraTransitionListener(transitionListener)); - updateCompassListenerState(true); - } - - /** - * Used to reset camera animators and notify listeners when the transition finishes. - */ - private class CameraTransitionListener implements OnLocationCameraTransitionListener { - - private final OnLocationCameraTransitionListener externalListener; - - private CameraTransitionListener(OnLocationCameraTransitionListener externalListener) { - this.externalListener = externalListener; - } - - @Override - public void onLocationCameraTransitionFinished(int cameraMode) { - if (externalListener != null) { - externalListener.onLocationCameraTransitionFinished(cameraMode); - } - reset(cameraMode); - } - - @Override - public void onLocationCameraTransitionCanceled(int cameraMode) { - if (externalListener != null) { - externalListener.onLocationCameraTransitionCanceled(cameraMode); - } - reset(cameraMode); - } - - private void reset(@CameraMode.Mode int cameraMode) { - locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(), - cameraMode == CameraMode.TRACKING_GPS_NORTH); - } - } - - /** - * Provides the current camera mode being used to track the location or compass updates. - * - * @return the current camera mode - */ - @CameraMode.Mode - public int getCameraMode() { - checkActivationState(); - return locationCameraController.getCameraMode(); - } - - /** - * Sets the render mode, which determines how the location updates will be rendered on the map. - *

- *

    - *
  • {@link RenderMode#NORMAL}: Shows user location, bearing ignored
  • - *
  • {@link RenderMode#COMPASS}: Shows user location with bearing considered from compass
  • - *
  • {@link RenderMode#GPS}: Shows user location with bearing considered from location
  • - *
- * - * @param renderMode one of the modes found in {@link RenderMode} - */ - public void setRenderMode(@RenderMode.Mode int renderMode) { - checkActivationState(); - locationLayerController.setRenderMode(renderMode); - updateLayerOffsets(true); - updateCompassListenerState(true); - } - - /** - * Provides the current render mode being used to show - * the location and/or compass updates on the map. - * - * @return the current render mode - */ - @RenderMode.Mode - public int getRenderMode() { - checkActivationState(); - return locationLayerController.getRenderMode(); - } - - /** - * Returns the current location options being used. - * - * @return the current {@link LocationComponentOptions} - */ - public LocationComponentOptions getLocationComponentOptions() { - checkActivationState(); - return options; - } - - /** - * Apply a new component style with a style resource. - * - * @param styleRes a XML style overriding some or all the options - */ - public void applyStyle(@NonNull Context context, @StyleRes int styleRes) { - checkActivationState(); - applyStyle(LocationComponentOptions.createFromAttributes(context, styleRes)); - } - - /** - * Apply a new component style with location component options. - * - * @param options to update the current style - */ - public void applyStyle(@NonNull final LocationComponentOptions options) { - checkActivationState(); - LocationComponent.this.options = options; - if (mapboxMap.getStyle() != null) { - locationLayerController.applyStyle(options); - locationCameraController.initializeOptions(options); - staleStateManager.setEnabled(options.enableStaleState()); - staleStateManager.setDelayTime(options.staleStateTimeout()); - locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options.trackingAnimationDurationMultiplier()); - locationAnimatorCoordinator.setCompassAnimationEnabled(options.compassAnimationEnabled()); - locationAnimatorCoordinator.setAccuracyAnimationEnabled(options.accuracyAnimationEnabled()); - updateMapWithOptions(options); - } - } - - /** - * Zooms to the desired zoom level. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - *

- * If the camera is transitioning when the zoom change is requested, the call is going to be ignored. - * Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument. - *

- * - * @param zoomLevel The desired zoom level. - * @param animationDuration The zoom animation duration. - * @param callback The callback with finish/cancel information - */ - public void zoomWhileTracking(double zoomLevel, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - checkActivationState(); - if (!isLayerReady) { - return; - } else if (getCameraMode() == CameraMode.NONE) { - Logger.e(TAG, String.format("%s%s", - "LocationComponent#zoomWhileTracking method can only be used", - " when a camera mode other than CameraMode#NONE is engaged.")); - return; - } else if (locationCameraController.isTransitioning()) { - Logger.e(TAG, - "LocationComponent#zoomWhileTracking method call is ignored because the camera mode is transitioning"); - return; - } - locationAnimatorCoordinator.feedNewZoomLevel(zoomLevel, mapboxMap.getCameraPosition(), animationDuration, callback); - } - - /** - * Zooms to the desired zoom level. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - *

- * If the camera is transitioning when the zoom change is requested, the call is going to be ignored. - * Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument. - *

- * - * @param zoomLevel The desired zoom level. - * @param animationDuration The zoom animation duration. - */ - public void zoomWhileTracking(double zoomLevel, long animationDuration) { - checkActivationState(); - zoomWhileTracking(zoomLevel, animationDuration, null); - } - - /** - * Zooms to the desired zoom level. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - *

- * If the camera is transitioning when the zoom change is requested, the call is going to be ignored. - * Use {@link CameraTransitionListener} to chain the animations, or provide the zoom as a camera change argument. - *

- * - * @param zoomLevel The desired zoom level. - */ - public void zoomWhileTracking(double zoomLevel) { - checkActivationState(); - zoomWhileTracking(zoomLevel, DEFAULT_TRACKING_ZOOM_ANIM_DURATION, null); - } - - /** - * Cancels animation started by {@link #zoomWhileTracking(double, long, MapboxMap.CancelableCallback)}. - */ - public void cancelZoomWhileTrackingAnimation() { - checkActivationState(); - locationAnimatorCoordinator.cancelZoomAnimation(); - } - - /** - * Tilts the camera. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - *

- * If the camera is transitioning when the tilt change is requested, the call is going to be ignored. - * Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument. - *

- * - * @param tilt The desired camera tilt. - * @param animationDuration The tilt animation duration. - * @param callback The callback with finish/cancel information - */ - public void tiltWhileTracking(double tilt, long animationDuration, - @Nullable MapboxMap.CancelableCallback callback) { - checkActivationState(); - if (!isLayerReady) { - return; - } else if (getCameraMode() == CameraMode.NONE) { - Logger.e(TAG, String.format("%s%s", - "LocationComponent#tiltWhileTracking method can only be used", - " when a camera mode other than CameraMode#NONE is engaged.")); - return; - } else if (locationCameraController.isTransitioning()) { - Logger.e(TAG, - "LocationComponent#tiltWhileTracking method call is ignored because the camera mode is transitioning"); - return; - } - locationAnimatorCoordinator.feedNewTilt(tilt, mapboxMap.getCameraPosition(), animationDuration, callback); - } - - /** - * Tilts the camera. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - *

- * If the camera is transitioning when the tilt change is requested, the call is going to be ignored. - * Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument. - *

- * - * @param tilt The desired camera tilt. - * @param animationDuration The tilt animation duration. - */ - public void tiltWhileTracking(double tilt, long animationDuration) { - checkActivationState(); - tiltWhileTracking(tilt, animationDuration, null); - } - - /** - * Tilts the camera. - * This API can only be used in pair with camera modes other than {@link CameraMode#NONE}. - * If you are not using any of {@link CameraMode} modes, - * use one of {@link MapboxMap#moveCamera(CameraUpdate)}, - * {@link MapboxMap#easeCamera(CameraUpdate)} or {@link MapboxMap#animateCamera(CameraUpdate)} instead. - *

- * If the camera is transitioning when the tilt change is requested, the call is going to be ignored. - * Use {@link CameraTransitionListener} to chain the animations, or provide the tilt as a camera change argument. - *

- * - * @param tilt The desired camera tilt. - */ - public void tiltWhileTracking(double tilt) { - checkActivationState(); - tiltWhileTracking(tilt, DEFAULT_TRACKING_TILT_ANIM_DURATION, null); - } - - /** - * Cancels animation started by {@link #tiltWhileTracking(double, long, MapboxMap.CancelableCallback)}. - */ - public void cancelTiltWhileTrackingAnimation() { - checkActivationState(); - locationAnimatorCoordinator.cancelTiltAnimation(); - } - - /** - * Use to either force a location update or to manually control when the user location gets - * updated. - * - * @param location where the location icon is placed on the map - */ - public void forceLocationUpdate(@Nullable Location location) { - checkActivationState(); - updateLocation(location, false); - } - - /** - * Set max FPS at which location animators can output updates. The throttling will only impact the location puck - * and camera tracking smooth animations. - *

- * Setting this will not impact any other animations schedule with {@link MapboxMap}, gesture animations or - * {@link #zoomWhileTracking(double)}/{@link #tiltWhileTracking(double)}. - *

- * Use this setting to limit animation rate of the location puck on higher zoom levels to decrease the stress on - * the device's CPU which can directly improve battery life, without sacrificing UX. - *

- * Example usage: - *

-   * {@code
-   * mapboxMap.addOnCameraIdleListener(new MapboxMap.OnCameraIdleListener() {
-   *   {@literal @}Override
-   *   public void onCameraIdle() {
-   *     double zoom = mapboxMap.getCameraPosition().zoom;
-   *     int maxAnimationFps;
-   *     if (zoom < 5) {
-   *       maxAnimationFps = 3;
-   *     } else if (zoom < 10) {
-   *       maxAnimationFps = 5;
-   *     } else if (zoom < 15) {
-   *       maxAnimationFps = 7;
-   *     } else if (zoom < 18) {
-   *       maxAnimationFps = 15;
-   *     } else {
-   *       maxAnimationFps = Integer.MAX_VALUE;
-   *     }
-   *     locationComponent.setMaxAnimationFps(maxAnimationFps);
-   *   }
-   * });
-   * }
-   * 
- *

- * If you're looking for a way to throttle the FPS of the whole map, including other animations and gestures, see - * {@link MapView#setMaximumFps(int)}. - * - * @param maxAnimationFps max location animation FPS - */ - public void setMaxAnimationFps(int maxAnimationFps) { - checkActivationState(); - locationAnimatorCoordinator.setMaxAnimationFps(maxAnimationFps); - } - - /** - * Set the location engine to update the current user location. - *

- * If {@code null} is passed in, all updates will have to occur through the - * {@link LocationComponent#forceLocationUpdate(Location)} method. - * - * @param locationEngine a {@link LocationEngine} this component should use to handle updates - */ - @SuppressLint("MissingPermission") - public void setLocationEngine(@Nullable LocationEngine locationEngine) { - checkActivationState(); - if (this.locationEngine != null) { - // If internal location engines being used, extra steps need to be taken to deconstruct the instance. - this.locationEngine.removeLocationUpdates(currentLocationEngineListener); - this.locationEngine = null; - } - - if (locationEngine != null) { - fastestInterval = locationEngineRequest.getFastestInterval(); - this.locationEngine = locationEngine; - if (isLayerReady && isEnabled) { - setLastLocation(); - locationEngine.requestLocationUpdates( - locationEngineRequest, currentLocationEngineListener, Looper.getMainLooper()); - } - } else { - fastestInterval = 0; - } - } - - /** - * Set the location request that's going to be used when requesting location updates. - * - * @param locationEngineRequest the location request - */ - public void setLocationEngineRequest(@NonNull LocationEngineRequest locationEngineRequest) { - checkActivationState(); - this.locationEngineRequest = locationEngineRequest; - - // reset internal LocationEngine ref to re-request location updates if needed - setLocationEngine(locationEngine); - } - - /** - * Get the location request that's going to be used when requesting location updates. - */ - @NonNull - public LocationEngineRequest getLocationEngineRequest() { - checkActivationState(); - return locationEngineRequest; - } - - /** - * Returns the current {@link LocationEngine} being used for updating the user location. - * - * @return the {@link LocationEngine} being used to update the user location - */ - @Nullable - public LocationEngine getLocationEngine() { - checkActivationState(); - return locationEngine; - } - - /** - * Sets the compass engine used to provide compass heading values. - * - * @param compassEngine to be used - */ - public void setCompassEngine(@Nullable CompassEngine compassEngine) { - checkActivationState(); - if (this.compassEngine != null) { - updateCompassListenerState(false); - } - this.compassEngine = compassEngine; - updateCompassListenerState(true); - } - - /** - * Returns the compass engine used to provide compass heading values. - * - * @return compass engine currently being used - */ - @Nullable - public CompassEngine getCompassEngine() { - checkActivationState(); - return compassEngine; - } - - /** - * Get the last know location of the location component. - * - * @return the last known location - */ - @Nullable - public Location getLastKnownLocation() { - checkActivationState(); - return lastLocation; - } - - /** - * Adds a listener that gets invoked when the user clicks the displayed location. - *

- * If there are registered location click listeners and the location is clicked, - * only {@link OnLocationClickListener#onLocationComponentClick()} is going to be delivered, - * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnMapClickListener#onMapClick(LatLng)} is going to be consumed - * and not pushed to the listeners registered after the component's activation. - * - * @param listener The location click listener that is invoked when the - * location is clicked - */ - public void addOnLocationClickListener(@NonNull OnLocationClickListener listener) { - onLocationClickListeners.add(listener); - } - - /** - * Removes the passed listener from the current list of location click listeners. - * - * @param listener to be removed - */ - public void removeOnLocationClickListener(@NonNull OnLocationClickListener listener) { - onLocationClickListeners.remove(listener); - } - - /** - * Adds a listener that gets invoked when the user long clicks the displayed location. - *

- * If there are registered location long click listeners and the location is long clicked, - * only {@link OnLocationLongClickListener#onLocationComponentLongClick()} is going to be delivered, - * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnMapLongClickListener#onMapLongClick(LatLng)} is going to be consumed - * and not pushed to the listeners registered after the component's activation. - * - * @param listener The location click listener that is invoked when the - * location is clicked - */ - public void addOnLocationLongClickListener(@NonNull OnLocationLongClickListener listener) { - onLocationLongClickListeners.add(listener); - } - - /** - * Removes the passed listener from the current list of location long click listeners. - * - * @param listener to be removed - */ - public void removeOnLocationLongClickListener(@NonNull OnLocationLongClickListener listener) { - onLocationLongClickListeners.remove(listener); - } - - /** - * Adds a listener that gets invoked when camera tracking state changes. - * - * @param listener Listener that gets invoked when camera tracking state changes. - */ - public void addOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) { - onCameraTrackingChangedListeners.add(listener); - } - - /** - * Removes a listener that gets invoked when camera tracking state changes. - * - * @param listener Listener that gets invoked when camera tracking state changes. - */ - public void removeOnCameraTrackingChangedListener(@NonNull OnCameraTrackingChangedListener listener) { - onCameraTrackingChangedListeners.remove(listener); - } - - /** - * Adds a listener that gets invoked when render mode changes. - * - * @param listener Listener that gets invoked when render mode changes. - */ - public void addOnRenderModeChangedListener(@NonNull OnRenderModeChangedListener listener) { - onRenderModeChangedListeners.add(listener); - } - - /** - * Removes a listener that gets invoked when render mode changes. - * - * @param listener Listener that gets invoked when render mode changes. - */ - public void removeRenderModeChangedListener(@NonNull OnRenderModeChangedListener listener) { - onRenderModeChangedListeners.remove(listener); - } - - /** - * Adds the passed listener that gets invoked when user updates have stopped long enough for the last update - * to be considered stale. - *

- * This timeout is set by {@link LocationComponentOptions#staleStateTimeout()}. - * - * @param listener invoked when last update is considered stale - */ - public void addOnLocationStaleListener(@NonNull OnLocationStaleListener listener) { - onLocationStaleListeners.add(listener); - } - - /** - * Removes the passed listener from the current list of stale listeners. - * - * @param listener to be removed from the list - */ - public void removeOnLocationStaleListener(@NonNull OnLocationStaleListener listener) { - onLocationStaleListeners.remove(listener); - } - - /** - * Internal use. - */ - public void onStart() { - isComponentStarted = true; - onLocationLayerStart(); - } - - /** - * Internal use. - */ - public void onStop() { - onLocationLayerStop(); - isComponentStarted = false; - } - - /** - * Internal use. - */ - public void onDestroy() { - } - - /** - * Internal use. - */ - public void onStartLoadingMap() { - onLocationLayerStop(); - } - - /** - * Internal use. - */ - public void onFinishLoadingStyle() { - if (isComponentInitialized) { - style = mapboxMap.getStyle(); - locationLayerController.initializeComponents(style, options); - locationCameraController.initializeOptions(options); - onLocationLayerStart(); - } - } - - @SuppressLint("MissingPermission") - private void onLocationLayerStart() { - if (!isComponentInitialized || !isComponentStarted || mapboxMap.getStyle() == null) { - return; - } - - if (!isLayerReady) { - isLayerReady = true; - mapboxMap.addOnCameraMoveListener(onCameraMoveListener); - mapboxMap.addOnCameraIdleListener(onCameraIdleListener); - if (options.enableStaleState()) { - staleStateManager.onStart(); - } - } - - if (isEnabled) { - if (locationEngine != null) { - try { - locationEngine.requestLocationUpdates( - locationEngineRequest, currentLocationEngineListener, Looper.getMainLooper()); - } catch (SecurityException se) { - Logger.e(TAG, "Unable to request location updates", se); - } - } - setCameraMode(locationCameraController.getCameraMode()); - setLastLocation(); - updateCompassListenerState(true); - setLastCompassHeading(); - } - } - - private void onLocationLayerStop() { - if (!isComponentInitialized || !isLayerReady || !isComponentStarted) { - return; - } - - isLayerReady = false; - staleStateManager.onStop(); - if (compassEngine != null) { - updateCompassListenerState(false); - } - locationAnimatorCoordinator.cancelAllAnimations(); - if (locationEngine != null) { - locationEngine.removeLocationUpdates(currentLocationEngineListener); - } - mapboxMap.removeOnCameraMoveListener(onCameraMoveListener); - mapboxMap.removeOnCameraIdleListener(onCameraIdleListener); - } - - private void initialize(@NonNull final Context context, @NonNull Style style, - @NonNull final LocationComponentOptions options) { - if (isComponentInitialized) { - return; - } - isComponentInitialized = true; - - if (!style.isFullyLoaded()) { - throw new IllegalStateException("Style is invalid, provide the most recently loaded one."); - } - - this.style = style; - this.options = options; - - mapboxMap.addOnMapClickListener(onMapClickListener); - mapboxMap.addOnMapLongClickListener(onMapLongClickListener); - - LayerSourceProvider sourceProvider = new LayerSourceProvider(); - LayerFeatureProvider featureProvider = new LayerFeatureProvider(); - LayerBitmapProvider bitmapProvider = new LayerBitmapProvider(context); - locationLayerController = new LocationLayerController(mapboxMap, style, sourceProvider, featureProvider, - bitmapProvider, options, renderModeChangedListener); - locationCameraController = new LocationCameraController( - context, mapboxMap, transform, cameraTrackingChangedListener, options, onCameraMoveInvalidateListener); - - locationAnimatorCoordinator = new LocationAnimatorCoordinator( - mapboxMap.getProjection(), - MapboxAnimatorSetProvider.getInstance(), - MapboxAnimatorProvider.getInstance() - ); - locationAnimatorCoordinator.setTrackingAnimationDurationMultiplier(options - .trackingAnimationDurationMultiplier()); - - WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); - if (windowManager != null && sensorManager != null) { - compassEngine = new LocationComponentCompassEngine(windowManager, sensorManager); - } - staleStateManager = new StaleStateManager(onLocationStaleListener, options); - - updateMapWithOptions(options); - - setRenderMode(RenderMode.NORMAL); - setCameraMode(CameraMode.NONE); - - onLocationLayerStart(); - } - - private void initializeLocationEngine(@NonNull Context context) { - if (this.locationEngine != null) { - this.locationEngine.removeLocationUpdates(currentLocationEngineListener); - } - setLocationEngine(internalLocationEngineProvider.getBestLocationEngine(context, false)); - } - - private void updateCompassListenerState(boolean canListen) { - if (compassEngine != null) { - if (!canListen) { - // We shouldn't listen, simply unregistering - removeCompassListener(compassEngine); - return; - } - - if (!isComponentInitialized || !isComponentStarted || !isEnabled) { - return; - } - - if (locationCameraController.isConsumingCompass() || locationLayerController.isConsumingCompass()) { - // If we have a consumer, and not yet listening, then start listening - if (!isListeningToCompass) { - isListeningToCompass = true; - compassEngine.addCompassListener(compassListener); - } - } else { - // If we have no consumers, stop listening - removeCompassListener(compassEngine); - } - } - } - - private void removeCompassListener(@NonNull CompassEngine engine) { - if (isListeningToCompass) { - isListeningToCompass = false; - engine.removeCompassListener(compassListener); - } - } - - private void enableLocationComponent() { - isEnabled = true; - onLocationLayerStart(); - } - - private void disableLocationComponent() { - isEnabled = false; - locationLayerController.hide(); - onLocationLayerStop(); - } - - private void updateMapWithOptions(@NonNull LocationComponentOptions options) { - int[] padding = options.padding(); - if (padding != null) { - mapboxMap.setPadding( - padding[0], padding[1], padding[2], padding[3] - ); - } - } - - /** - * Updates the user location icon. - * - * @param location the latest user location - */ - private void updateLocation(@Nullable final Location location, boolean fromLastLocation) { - if (location == null) { - return; - } else if (!isLayerReady) { - lastLocation = location; - return; - } else { - long currentTime = SystemClock.elapsedRealtime(); - if (currentTime - lastUpdateTime < fastestInterval) { - return; - } else { - lastUpdateTime = currentTime; - } - } - - showLocationLayerIfHidden(); - - if (!fromLastLocation) { - staleStateManager.updateLatestLocationTime(); - } - CameraPosition currentCameraPosition = mapboxMap.getCameraPosition(); - boolean isGpsNorth = getCameraMode() == CameraMode.TRACKING_GPS_NORTH; - locationAnimatorCoordinator.feedNewLocation(location, currentCameraPosition, isGpsNorth); - updateAccuracyRadius(location, false); - lastLocation = location; - } - - private void showLocationLayerIfHidden() { - boolean isLocationLayerHidden = locationLayerController.isHidden(); - if (isEnabled && isComponentStarted && isLocationLayerHidden) { - locationLayerController.show(); - } - } - - private void updateCompassHeading(float heading) { - locationAnimatorCoordinator.feedNewCompassBearing(heading, mapboxMap.getCameraPosition()); - } - - /** - * If the locationEngine contains a last location value, we use it for the initial location layer - * position. - */ - @SuppressLint("MissingPermission") - private void setLastLocation() { - if (locationEngine != null) { - locationEngine.getLastLocation(lastLocationEngineListener); - } else { - updateLocation(getLastKnownLocation(), true); - } - } - - private void setLastCompassHeading() { - updateCompassHeading(compassEngine != null ? compassEngine.getLastHeading() : 0); - } - - @SuppressLint("MissingPermission") - private void updateLayerOffsets(boolean forceUpdate) { - CameraPosition position = mapboxMap.getCameraPosition(); - if (lastCameraPosition == null || forceUpdate) { - lastCameraPosition = position; - locationLayerController.updateForegroundBearing((float) position.bearing); - locationLayerController.updateForegroundOffset(position.tilt); - updateAccuracyRadius(getLastKnownLocation(), true); - return; - } - - if (position.bearing != lastCameraPosition.bearing) { - locationLayerController.updateForegroundBearing((float) position.bearing); - } - if (position.tilt != lastCameraPosition.tilt) { - locationLayerController.updateForegroundOffset(position.tilt); - } - if (position.zoom != lastCameraPosition.zoom) { - updateAccuracyRadius(getLastKnownLocation(), true); - } - lastCameraPosition = position; - } - - private void updateAccuracyRadius(Location location, boolean noAnimation) { - locationAnimatorCoordinator.feedNewAccuracyRadius(Utils.calculateZoomLevelRadius(mapboxMap, location), noAnimation); - } - - private void updateAnimatorListenerHolders() { - Set animationsValueChangeListeners = new HashSet<>(); - animationsValueChangeListeners.addAll(locationLayerController.getAnimationListeners()); - animationsValueChangeListeners.addAll(locationCameraController.getAnimationListeners()); - locationAnimatorCoordinator.updateAnimatorListenerHolders(animationsValueChangeListeners); - locationAnimatorCoordinator.resetAllCameraAnimations(mapboxMap.getCameraPosition(), - locationCameraController.getCameraMode() == CameraMode.TRACKING_GPS_NORTH); - locationAnimatorCoordinator.resetAllLayerAnimations(); - } - - @NonNull - private OnCameraMoveListener onCameraMoveListener = new OnCameraMoveListener() { - @Override - public void onCameraMove() { - updateLayerOffsets(false); - } - }; - - @NonNull - private OnCameraIdleListener onCameraIdleListener = new OnCameraIdleListener() { - @Override - public void onCameraIdle() { - updateLayerOffsets(false); - } - }; - - @NonNull - private OnMapClickListener onMapClickListener = new OnMapClickListener() { - @Override - public boolean onMapClick(@NonNull LatLng point) { - if (!onLocationClickListeners.isEmpty() && locationLayerController.onMapClick(point)) { - for (OnLocationClickListener listener : onLocationClickListeners) { - listener.onLocationComponentClick(); - } - return true; - } - return false; - } - }; - - @NonNull - private MapboxMap.OnMapLongClickListener onMapLongClickListener = new MapboxMap.OnMapLongClickListener() { - @Override - public boolean onMapLongClick(@NonNull LatLng point) { - if (!onLocationLongClickListeners.isEmpty() && locationLayerController.onMapClick(point)) { - for (OnLocationLongClickListener listener : onLocationLongClickListeners) { - listener.onLocationComponentLongClick(); - } - return true; - } - return false; - } - }; - - @NonNull - private OnLocationStaleListener onLocationStaleListener = new OnLocationStaleListener() { - @Override - public void onStaleStateChange(boolean isStale) { - locationLayerController.setLocationsStale(isStale); - - for (OnLocationStaleListener listener : onLocationStaleListeners) { - listener.onStaleStateChange(isStale); - } - } - }; - - @NonNull - private OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = new OnCameraMoveInvalidateListener() { - @Override - public void onInvalidateCameraMove() { - onCameraMoveListener.onCameraMove(); - } - }; - - @NonNull - private CompassListener compassListener = new CompassListener() { - @Override - public void onCompassChanged(float userHeading) { - updateCompassHeading(userHeading); - } - - @Override - public void onCompassAccuracyChange(int compassStatus) { - // Currently don't handle this inside SDK - } - }; - - @VisibleForTesting - static final class CurrentLocationEngineCallback implements LocationEngineCallback { - private final WeakReference componentWeakReference; - - CurrentLocationEngineCallback(LocationComponent component) { - this.componentWeakReference = new WeakReference<>(component); - } - - @Override - public void onSuccess(LocationEngineResult result) { - LocationComponent component = componentWeakReference.get(); - if (component != null) { - component.updateLocation(result.getLastLocation(), false); - } - } - - @Override - public void onFailure(@NonNull Exception exception) { - Logger.e(TAG, "Failed to obtain location update", exception); - } - } - - @VisibleForTesting - static final class LastLocationEngineCallback implements LocationEngineCallback { - private final WeakReference componentWeakReference; - - LastLocationEngineCallback(LocationComponent component) { - this.componentWeakReference = new WeakReference<>(component); - } - - @Override - public void onSuccess(LocationEngineResult result) { - LocationComponent component = componentWeakReference.get(); - if (component != null) { - component.updateLocation(result.getLastLocation(), true); - } - } - - @Override - public void onFailure(@NonNull Exception exception) { - Logger.e(TAG, "Failed to obtain last location update", exception); - } - } - - @NonNull - @VisibleForTesting - OnCameraTrackingChangedListener cameraTrackingChangedListener = new OnCameraTrackingChangedListener() { - @Override - public void onCameraTrackingDismissed() { - for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { - listener.onCameraTrackingDismissed(); - } - } - - @Override - public void onCameraTrackingChanged(int currentMode) { - locationAnimatorCoordinator.cancelZoomAnimation(); - locationAnimatorCoordinator.cancelTiltAnimation(); - updateAnimatorListenerHolders(); - for (OnCameraTrackingChangedListener listener : onCameraTrackingChangedListeners) { - listener.onCameraTrackingChanged(currentMode); - } - } - }; - - @NonNull - @VisibleForTesting - OnRenderModeChangedListener renderModeChangedListener = new OnRenderModeChangedListener() { - @Override - public void onRenderModeChanged(int currentMode) { - updateAnimatorListenerHolders(); - for (OnRenderModeChangedListener listener : onRenderModeChangedListeners) { - listener.onRenderModeChanged(currentMode); - } - } - }; - - @NonNull - private final MapboxMap.OnDeveloperAnimationListener developerAnimationListener = - new MapboxMap.OnDeveloperAnimationListener() { - @Override - public void onDeveloperAnimationStarted() { - if (isComponentInitialized && isEnabled) { - setCameraMode(CameraMode.NONE); - } - } - }; - - static class InternalLocationEngineProvider { - LocationEngine getBestLocationEngine(@NonNull Context context, boolean background) { - return LocationEngineProvider.getBestLocationEngine(context, background); - } - } - - private void checkActivationState() { - if (!isComponentInitialized) { - throw new LocationComponentNotInitializedException(); - } - } - - /** - * Returns whether the location component is activated. - * - * @return true if the component is activated, false otherwise - */ - public boolean isLocationComponentActivated() { - return isComponentInitialized; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptions.java deleted file mode 100644 index 04987621e..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptions.java +++ /dev/null @@ -1,244 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.android.core.location.LocationEngine; -import com.mapbox.android.core.location.LocationEngineRequest; -import com.mapbox.mapboxsdk.maps.Style; - -/** - * A class which holds the various options for activating the Maps SDK's {@link LocationComponent} to eventually - * show the device location on the map. - */ -public class LocationComponentActivationOptions { - - private final Context context; - private final Style style; - private final LocationEngine locationEngine; - private final LocationEngineRequest locationEngineRequest; - private final LocationComponentOptions locationComponentOptions; - private final int styleRes; - private final boolean useDefaultLocationEngine; - - private LocationComponentActivationOptions(@NonNull Context context, @NonNull Style style, - @Nullable LocationEngine locationEngine, - @Nullable LocationEngineRequest locationEngineRequest, - @Nullable LocationComponentOptions locationComponentOptions, - int styleRes, - boolean useDefaultLocationEngine) { - this.context = context; - this.style = style; - this.locationEngine = locationEngine; - this.locationEngineRequest = locationEngineRequest; - this.locationComponentOptions = locationComponentOptions; - this.styleRes = styleRes; - this.useDefaultLocationEngine = useDefaultLocationEngine; - } - - /** - * Convenience method to retrieve a {@link LocationComponentActivationOptions} object which is ready to build with - * - * @return a builder object - */ - @NonNull - public static Builder builder(@NonNull Context context, @NonNull Style fullyLoadedMapStyle) { - return new LocationComponentActivationOptions.Builder(context, fullyLoadedMapStyle); - } - - /** - * The application's current context - * - * @return the application's current context - */ - @NonNull - public Context context() { - return context; - } - - /** - * The proxy object for current map style. More info at {@link Style} - * - * @return the map's fully loaded Style object - */ - @NonNull - public Style style() { - return style; - } - - /** - * The {@link LocationEngine} which the {@link LocationComponent} should use - * - * @return the engine - */ - @Nullable - public LocationEngine locationEngine() { - return locationEngine; - } - - /** - * The location request which the {@link LocationComponent} should use - * - * @return the LocationEngineRequest object - */ - @Nullable - public LocationEngineRequest locationEngineRequest() { - return locationEngineRequest; - } - - /** - * A built {@link LocationComponentOptions} object, which holds the various {@link LocationComponent} styling options - * - * @return the options for styling the actual LocationComponent - */ - @Nullable - public LocationComponentOptions locationComponentOptions() { - return locationComponentOptions; - } - - /** - * The LocationComponent style resource (e.g. R.style.style_name) - * - * @return the style resource. - */ - public int styleRes() { - return styleRes; - } - - /** - * True if you want to initialize and use the built-in location engine or false if there should be no - * location engine initialized - * - * @return whether the default LocationEngine is used - */ - public boolean useDefaultLocationEngine() { - return useDefaultLocationEngine; - } - - /** - * Builder class for constructing a new instance of {@link LocationComponentActivationOptions}. - */ - public static class Builder { - private final Context context; - private final Style style; - private LocationEngine locationEngine; - private LocationEngineRequest locationEngineRequest; - private LocationComponentOptions locationComponentOptions; - private int styleRes; - - /** - * Set to true as the default in case a true/false value isn't declared via the builder's - * {@link LocationComponentActivationOptions#useDefaultLocationEngine()} method. - *

- * Please be aware that this activation boolean is ignored when a non-null - * {@link LocationEngine} is set via the builder's `locationEngine()` method. - */ - private boolean useDefaultLocationEngine = true; - - /** - * Constructor for the {@link LocationComponentActivationOptions} builder class. - * While other activation options are optional, the activation process always requires - * context and a fully-loaded map {@link Style} object, which is why the two are in this - * constructor. - */ - public Builder(@NonNull Context context, @NonNull Style style) { - this.context = context; - this.style = style; - } - - /** - * Deliver your own {@link LocationEngine} to the LocationComponent. - *

- * The true/false - * {@link LocationComponentActivationOptions.Builder#useDefaultLocationEngine()} - * activation option is ignored when a non-null {@link LocationEngine} is set via - * this `locationEngine()` method. - * - * @param locationEngine a {@link LocationEngine} object - * @return the {@link Builder} object being constructed - */ - @NonNull - public Builder locationEngine(@Nullable LocationEngine locationEngine) { - this.locationEngine = locationEngine; - return this; - } - - /** - * @param locationEngineRequest the location request which the - * {@link LocationComponent} should use - * @return the {@link Builder} object being constructed - */ - public Builder locationEngineRequest(LocationEngineRequest locationEngineRequest) { - this.locationEngineRequest = locationEngineRequest; - return this; - } - - /** - * @param locationComponentOptions a built {@link LocationComponentOptions} object, - * which holds the various {@link LocationComponent} - * styling options - * @return the {@link Builder} object being constructed - */ - public Builder locationComponentOptions(LocationComponentOptions locationComponentOptions) { - this.locationComponentOptions = locationComponentOptions; - return this; - } - - /** - * @param styleRes the LocationComponent style resource (e.g. R.style.style_name) - * @return the {@link Builder} object being constructed - */ - public Builder styleRes(int styleRes) { - this.styleRes = styleRes; - return this; - } - - /** - * @param useDefaultLocationEngine true if you want to initialize and use the - * built-in location engine or false if there - * should be no location engine initialized - * This is ignored when null is set as the parameter - * for {@link LocationComponentActivationOptions#builder - * (Context, Style)#locationEngine()}. - * @return the {@link Builder} object being constructed - */ - public Builder useDefaultLocationEngine(boolean useDefaultLocationEngine) { - this.useDefaultLocationEngine = useDefaultLocationEngine; - return this; - } - - /** - * Method which actually builds the {@link LocationComponentActivationOptions} object while - * taking the various options into account. - * - * @return a built {@link LocationComponentActivationOptions} object - */ - public LocationComponentActivationOptions build() { - if (styleRes != 0 && locationComponentOptions != null) { - throw new IllegalArgumentException( - "You've provided both a style resource and a LocationComponentOptions object to the " - + "LocationComponentActivationOptions builder. You can't use both and " - + "you must choose one of the two to style the LocationComponent."); - } - if (context == null) { - throw new NullPointerException( - "Context in LocationComponentActivationOptions is null."); - } - if (style == null) { - throw new NullPointerException( - "Style in LocationComponentActivationOptions is null. Make sure the " - + "Style object isn't null. Wait for the map to fully load before passing " - + "the Style object to LocationComponentActivationOptions."); - } - if (!style.isFullyLoaded()) { - throw new IllegalArgumentException( - "Style in LocationComponentActivationOptions isn't fully loaded. Wait for the " - + "map to fully load before passing the Style object to " - + "LocationComponentActivationOptions."); - } - return new LocationComponentActivationOptions(context, style, locationEngine, - locationEngineRequest, locationComponentOptions, styleRes, useDefaultLocationEngine); - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java deleted file mode 100644 index ce3823303..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentCompassEngine.java +++ /dev/null @@ -1,268 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.SystemClock; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.Surface; -import android.view.WindowManager; - -import com.mapbox.mapboxsdk.log.Logger; - -import java.util.ArrayList; -import java.util.List; - -/** - * This manager class handles compass events such as starting the tracking of device bearing, or - * when a new compass update occurs. - */ -class LocationComponentCompassEngine implements CompassEngine, SensorEventListener { - - private static final String TAG = "Mbgl-LocationComponentCompassEngine"; - - // The rate sensor events will be delivered at. As the Android documentation states, this is only - // a hint to the system and the events might actually be received faster or slower then this - // specified rate. Since the minimum Android API levels about 9, we are able to set this value - // ourselves rather than using one of the provided constants which deliver updates too quickly for - // our use case. The default is set to 100ms - static final int SENSOR_DELAY_MICROS = 100 * 1000; - // Filtering coefficient 0 < ALPHA < 1 - private static final float ALPHA = 0.45f; - - @NonNull - private final WindowManager windowManager; - @NonNull - private final SensorManager sensorManager; - private final List compassListeners = new ArrayList<>(); - - // Not all devices have a compassSensor - @Nullable - private Sensor compassSensor; - @Nullable - private Sensor gravitySensor; - @Nullable - private Sensor magneticFieldSensor; - - @NonNull - private float[] truncatedRotationVectorValue = new float[4]; - @NonNull - private float[] rotationMatrix = new float[9]; - private float[] rotationVectorValue; - private float lastHeading; - private int lastAccuracySensorStatus; - - private long compassUpdateNextTimestamp; - @Nullable - private float[] gravityValues = new float[3]; - @Nullable - private float[] magneticValues = new float[3]; - - /** - * Construct a new instance of the this class. A internal compass listeners needed to separate it - * from the cleared list of public listeners. - */ - LocationComponentCompassEngine(@NonNull WindowManager windowManager, @NonNull SensorManager sensorManager) { - this.windowManager = windowManager; - this.sensorManager = sensorManager; - compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); - if (compassSensor == null) { - if (isGyroscopeAvailable()) { - Logger.d(TAG, "Rotation vector sensor not supported on device, " - + "falling back to orientation."); - compassSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); - } else { - Logger.d(TAG, "Rotation vector sensor not supported on device, " - + "falling back to accelerometer and magnetic field."); - gravitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - magneticFieldSensor = sensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); - } - } - } - - @Override - public void addCompassListener(@NonNull CompassListener compassListener) { - if (compassListeners.isEmpty()) { - registerSensorListeners(); - } - compassListeners.add(compassListener); - } - - @Override - public void removeCompassListener(@NonNull CompassListener compassListener) { - compassListeners.remove(compassListener); - if (compassListeners.isEmpty()) { - unregisterSensorListeners(); - } - } - - @Override - public int getLastAccuracySensorStatus() { - return lastAccuracySensorStatus; - } - - @Override - public float getLastHeading() { - return lastHeading; - } - - @Override - public void onSensorChanged(@NonNull SensorEvent event) { - // check when the last time the compass was updated, return if too soon. - long currentTime = SystemClock.elapsedRealtime(); - if (currentTime < compassUpdateNextTimestamp) { - return; - } - if (lastAccuracySensorStatus == SensorManager.SENSOR_STATUS_UNRELIABLE) { - Logger.d(TAG, "Compass sensor is unreliable, device calibration is needed."); - return; - } - if (event.sensor.getType() == Sensor.TYPE_ROTATION_VECTOR) { - rotationVectorValue = getRotationVectorFromSensorEvent(event); - updateOrientation(); - } else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { - notifyCompassChangeListeners((event.values[0] + 360) % 360); - } else if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { - gravityValues = lowPassFilter(getRotationVectorFromSensorEvent(event), gravityValues); - updateOrientation(); - } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { - magneticValues = lowPassFilter(getRotationVectorFromSensorEvent(event), magneticValues); - updateOrientation(); - } - - // Update the compassUpdateNextTimestamp - compassUpdateNextTimestamp = currentTime + LocationComponentConstants.COMPASS_UPDATE_RATE_MS; - } - - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { - if (lastAccuracySensorStatus != accuracy) { - for (CompassListener compassListener : compassListeners) { - compassListener.onCompassAccuracyChange(accuracy); - } - lastAccuracySensorStatus = accuracy; - } - } - - private boolean isGyroscopeAvailable() { - return sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) != null; - } - - @SuppressWarnings("SuspiciousNameCombination") - private void updateOrientation() { - if (rotationVectorValue != null) { - SensorManager.getRotationMatrixFromVector(rotationMatrix, rotationVectorValue); - } else { - // Get rotation matrix given the gravity and geomagnetic matrices - SensorManager.getRotationMatrix(rotationMatrix, null, gravityValues, magneticValues); - } - - final int worldAxisForDeviceAxisX; - final int worldAxisForDeviceAxisY; - - // Remap the axes as if the device screen was the instrument panel, - // and adjust the rotation matrix for the device orientation. - switch (windowManager.getDefaultDisplay().getRotation()) { - case Surface.ROTATION_90: - worldAxisForDeviceAxisX = SensorManager.AXIS_Z; - worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_X; - break; - case Surface.ROTATION_180: - worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_X; - worldAxisForDeviceAxisY = SensorManager.AXIS_MINUS_Z; - break; - case Surface.ROTATION_270: - worldAxisForDeviceAxisX = SensorManager.AXIS_MINUS_Z; - worldAxisForDeviceAxisY = SensorManager.AXIS_X; - break; - case Surface.ROTATION_0: - default: - worldAxisForDeviceAxisX = SensorManager.AXIS_X; - worldAxisForDeviceAxisY = SensorManager.AXIS_Z; - break; - } - - float[] adjustedRotationMatrix = new float[9]; - SensorManager.remapCoordinateSystem(rotationMatrix, worldAxisForDeviceAxisX, - worldAxisForDeviceAxisY, adjustedRotationMatrix); - - // Transform rotation matrix into azimuth/pitch/roll - float[] orientation = new float[3]; - SensorManager.getOrientation(adjustedRotationMatrix, orientation); - - // The x-axis is all we care about here. - notifyCompassChangeListeners((float) Math.toDegrees(orientation[0])); - } - - private void notifyCompassChangeListeners(float heading) { - for (CompassListener compassListener : compassListeners) { - compassListener.onCompassChanged(heading); - } - lastHeading = heading; - } - - private void registerSensorListeners() { - if (isCompassSensorAvailable()) { - // Does nothing if the sensors already registered. - sensorManager.registerListener(this, compassSensor, SENSOR_DELAY_MICROS); - } else { - sensorManager.registerListener(this, gravitySensor, SENSOR_DELAY_MICROS); - sensorManager.registerListener(this, magneticFieldSensor, SENSOR_DELAY_MICROS); - } - } - - private void unregisterSensorListeners() { - if (isCompassSensorAvailable()) { - sensorManager.unregisterListener(this, compassSensor); - } else { - sensorManager.unregisterListener(this, gravitySensor); - sensorManager.unregisterListener(this, magneticFieldSensor); - } - } - - private boolean isCompassSensorAvailable() { - return compassSensor != null; - } - - /** - * Helper function, that filters newValues, considering previous values - * - * @param newValues array of float, that contains new data - * @param smoothedValues array of float, that contains previous state - * @return float filtered array of float - */ - @NonNull - private float[] lowPassFilter(@NonNull float[] newValues, @Nullable float[] smoothedValues) { - if (smoothedValues == null) { - return newValues; - } - for (int i = 0; i < newValues.length; i++) { - smoothedValues[i] = smoothedValues[i] + ALPHA * (newValues[i] - smoothedValues[i]); - } - return smoothedValues; - } - - /** - * Pulls out the rotation vector from a SensorEvent, with a maximum length - * vector of four elements to avoid potential compatibility issues. - * - * @param event the sensor event - * @return the events rotation vector, potentially truncated - */ - @NonNull - private float[] getRotationVectorFromSensorEvent(@NonNull SensorEvent event) { - if (event.values.length > 4) { - // On some Samsung devices SensorManager.getRotationMatrixFromVector - // appears to throw an exception if rotation vector has length > 4. - // For the purposes of this class the first 4 values of the - // rotation vector are sufficient (see crbug.com/335298 for details). - // Only affects Android 4.3 - System.arraycopy(event.values, 0, truncatedRotationVectorValue, 0, 4); - return truncatedRotationVectorValue; - } else { - return event.values; - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java deleted file mode 100644 index f2158584c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentConstants.java +++ /dev/null @@ -1,94 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -/** - * Contains all the constants being used for the {@link LocationComponent}. - */ -public final class LocationComponentConstants { - - // Controls the compass update rate in milliseconds - static final long COMPASS_UPDATE_RATE_MS = 500; - - // Sets the transition animation duration when switching camera modes. - static final long TRANSITION_ANIMATION_DURATION_MS = 750; - - // Sets the max allowed time for the location icon animation from one LatLng to another. - static final long MAX_ANIMATION_DURATION_MS = 2000; - - // Sets the duration of change of accuracy radius when a different value is provided. - static final long ACCURACY_RADIUS_ANIMATION_DURATION = 250; - - // Default animation duration for zooming while tracking. - static final long DEFAULT_TRACKING_ZOOM_ANIM_DURATION = 750; - - // Default animation duration for tilting while tracking. - static final long DEFAULT_TRACKING_TILT_ANIM_DURATION = 1250; - - // Threshold value to perform immediate camera/layer position update. - static final double INSTANT_LOCATION_TRANSITION_THRESHOLD = 50_000; - - // Default interval between location updates - static final long DEFAULT_INTERVAL_MILLIS = 1000; - - // Default fastest acceptable interval between location updates - static final long DEFAULT_FASTEST_INTERVAL_MILLIS = 1000; - - // Sources - - /** - * Source ID of the location's GeoJsonSource. - */ - public static final String LOCATION_SOURCE = "mapbox-location-source"; - - static final String PROPERTY_GPS_BEARING = "mapbox-property-gps-bearing"; - static final String PROPERTY_COMPASS_BEARING = "mapbox-property-compass-bearing"; - static final String PROPERTY_LOCATION_STALE = "mapbox-property-location-stale"; - static final String PROPERTY_ACCURACY_RADIUS = "mapbox-property-accuracy-radius"; - static final String PROPERTY_ACCURACY_COLOR = "mapbox-property-accuracy-color"; - static final String PROPERTY_ACCURACY_ALPHA = "mapbox-property-accuracy-alpha"; - static final String PROPERTY_FOREGROUND_ICON_OFFSET = "mapbox-property-foreground-icon-offset"; - static final String PROPERTY_SHADOW_ICON_OFFSET = "mapbox-property-shadow-icon-offset"; - static final String PROPERTY_FOREGROUND_ICON = "mapbox-property-foreground-icon"; - static final String PROPERTY_BACKGROUND_ICON = "mapbox-property-background-icon"; - static final String PROPERTY_FOREGROUND_STALE_ICON = "mapbox-property-foreground-stale-icon"; - static final String PROPERTY_BACKGROUND_STALE_ICON = "mapbox-property-background-stale-icon"; - static final String PROPERTY_BEARING_ICON = "mapbox-property-shadow-icon"; - - // Layers - - /** - * Layer ID of the location shadow. - */ - public static final String SHADOW_LAYER = "mapbox-location-shadow-layer"; - - /** - * Layer ID of the location foreground icon. - */ - public static final String FOREGROUND_LAYER = "mapbox-location-foreground-layer"; - - /** - * Layer ID of the location background icon. - */ - public static final String BACKGROUND_LAYER = "mapbox-location-background-layer"; - - /** - * Layer ID of the location accuracy. - */ - public static final String ACCURACY_LAYER = "mapbox-location-accuracy-layer"; - - /** - * Layer ID of the location bearing icon. - */ - public static final String BEARING_LAYER = "mapbox-location-bearing-layer"; - - // Icons - static final String FOREGROUND_ICON = "mapbox-location-icon"; - static final String BACKGROUND_ICON = "mapbox-location-stroke-icon"; - static final String FOREGROUND_STALE_ICON = "mapbox-location-stale-icon"; - static final String BACKGROUND_STALE_ICON = "mapbox-location-background-stale-icon"; - static final String SHADOW_ICON = "mapbox-location-shadow-icon"; - static final String BEARING_ICON = "mapbox-location-bearing-icon"; - - private LocationComponentConstants() { - // Class should not be initialized - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentNotInitializedException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentNotInitializedException.java deleted file mode 100644 index 82a73b973..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentNotInitializedException.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -final class LocationComponentNotInitializedException extends RuntimeException { - LocationComponentNotInitializedException() { - super("The LocationComponent has to be activated with one of the LocationComponent#activateLocationComponent" - + " overloads before any other methods are invoked."); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java deleted file mode 100644 index 48c89622c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentOptions.java +++ /dev/null @@ -1,1778 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.ColorInt; -import android.support.annotation.Dimension; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StyleRes; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.style.layers.Layer; - -import java.util.Arrays; - -/** - * This class exposes options for the Location Component. The options can be set by defining a - * style in your apps style.xml file and passing in directly into the {@link LocationComponent} - * class. Alternatively, if properties need to be changed at runtime depending on a specific state, - * you can build an instance of this class, setting the values you desire, and then passing it into - * either the {@link LocationComponent} activation method (if it isn't initialized yet) or - * {@link LocationComponent#applyStyle(LocationComponentOptions)}. - *

- * When the {@link #createFromAttributes(Context, int)} methods called, any attributes not found - * inside the style will revert back to using their default set values. Likewise, when building a - * new {@link LocationComponentOptions} class using the builder, any options neglecting to be set will - * reset to their default values. - *

- * If you would like to keep your custom style changes while modifying a single attribute, you can - * get the currently used options object using {@link LocationComponent#getLocationComponentOptions()} - * and it's {@code toBuilder} method to modify a single entry while also maintaining the other - * settings. Once your modifications have been made, you'll need to pass it back into the location - * component using {@link LocationComponent#applyStyle(LocationComponentOptions)}. - */ -public class LocationComponentOptions implements Parcelable { - - /** - * Default accuracy alpha - */ - private static final float ACCURACY_ALPHA_DEFAULT = 0.15f; - - /** - * Default icon scale factor when the map is zoomed out - */ - private static final float MIN_ZOOM_ICON_SCALE_DEFAULT = 0.6f; - - /** - * Default icon scale factor when the map is zoomed in - */ - private static final float MAX_ZOOM_ICON_SCALE_DEFAULT = 1f; - - /** - * Default map padding - */ - private static final int[] PADDING_DEFAULT = {0, 0, 0, 0}; - - /** - * The default value which is used when the stale state is enabled - */ - private static final long STALE_STATE_DELAY_MS = 30_000L; - - /** - * Default animation duration multiplier - */ - private static final float TRACKING_ANIMATION_DURATION_MULTIPLIER_DEFAULT = 1.1f; - - private float accuracyAlpha; - private int accuracyColor; - private int backgroundDrawableStale; - @Nullable - private String backgroundStaleName; - private int foregroundDrawableStale; - @Nullable - private String foregroundStaleName; - private int gpsDrawable; - @Nullable - private String gpsName; - private int foregroundDrawable; - @Nullable - private String foregroundName; - private int backgroundDrawable; - @Nullable - private String backgroundName; - private int bearingDrawable; - @Nullable - private String bearingName; - @Nullable - private Integer bearingTintColor; - @Nullable - private Integer foregroundTintColor; - @Nullable - private Integer backgroundTintColor; - @Nullable - private Integer foregroundStaleTintColor; - @Nullable - private Integer backgroundStaleTintColor; - private float elevation; - private boolean enableStaleState; - private long staleStateTimeout; - @Nullable - private int[] padding; - private float maxZoomIconScale; - private float minZoomIconScale; - private boolean trackingGesturesManagement; - private float trackingInitialMoveThreshold; - private float trackingMultiFingerMoveThreshold; - private String layerAbove; - private String layerBelow; - private float trackingAnimationDurationMultiplier; - private boolean compassAnimationEnabled; - private boolean accuracyAnimationEnabled; - - public LocationComponentOptions( - float accuracyAlpha, - int accuracyColor, - int backgroundDrawableStale, - @Nullable String backgroundStaleName, - int foregroundDrawableStale, - @Nullable String foregroundStaleName, - int gpsDrawable, - @Nullable String gpsName, - int foregroundDrawable, - @Nullable String foregroundName, - int backgroundDrawable, - @Nullable String backgroundName, - int bearingDrawable, - @Nullable String bearingName, - @Nullable Integer bearingTintColor, - @Nullable Integer foregroundTintColor, - @Nullable Integer backgroundTintColor, - @Nullable Integer foregroundStaleTintColor, - @Nullable Integer backgroundStaleTintColor, - float elevation, - boolean enableStaleState, - long staleStateTimeout, - @Nullable int[] padding, - float maxZoomIconScale, - float minZoomIconScale, - boolean trackingGesturesManagement, - float trackingInitialMoveThreshold, - float trackingMultiFingerMoveThreshold, - String layerAbove, - String layerBelow, - float trackingAnimationDurationMultiplier, - boolean compassAnimationEnabled, - boolean accuracyAnimationEnabled) { - this.accuracyAlpha = accuracyAlpha; - this.accuracyColor = accuracyColor; - this.backgroundDrawableStale = backgroundDrawableStale; - this.backgroundStaleName = backgroundStaleName; - this.foregroundDrawableStale = foregroundDrawableStale; - this.foregroundStaleName = foregroundStaleName; - this.gpsDrawable = gpsDrawable; - this.gpsName = gpsName; - this.foregroundDrawable = foregroundDrawable; - this.foregroundName = foregroundName; - this.backgroundDrawable = backgroundDrawable; - this.backgroundName = backgroundName; - this.bearingDrawable = bearingDrawable; - this.bearingName = bearingName; - this.bearingTintColor = bearingTintColor; - this.foregroundTintColor = foregroundTintColor; - this.backgroundTintColor = backgroundTintColor; - this.foregroundStaleTintColor = foregroundStaleTintColor; - this.backgroundStaleTintColor = backgroundStaleTintColor; - this.elevation = elevation; - this.enableStaleState = enableStaleState; - this.staleStateTimeout = staleStateTimeout; - if (padding == null) { - throw new NullPointerException("Null padding"); - } - this.padding = padding; - this.maxZoomIconScale = maxZoomIconScale; - this.minZoomIconScale = minZoomIconScale; - this.trackingGesturesManagement = trackingGesturesManagement; - this.trackingInitialMoveThreshold = trackingInitialMoveThreshold; - this.trackingMultiFingerMoveThreshold = trackingMultiFingerMoveThreshold; - this.layerAbove = layerAbove; - this.layerBelow = layerBelow; - this.trackingAnimationDurationMultiplier = trackingAnimationDurationMultiplier; - this.compassAnimationEnabled = compassAnimationEnabled; - this.accuracyAnimationEnabled = accuracyAnimationEnabled; - } - - /** - * Construct a new Location Component Options class using the attributes found within a style - * resource. It's important to note that you only need to define the attributes you plan to - * change and can safely ignore the other attributes which will be set to their default value. - * - * @param context your activity's context used for acquiring resources - * @param styleRes the style id where your custom attributes are defined - * @return a new {@link LocationComponentOptions} object with the settings you defined in your style - * resource - */ - @NonNull - public static LocationComponentOptions createFromAttributes(@NonNull Context context, - @StyleRes int styleRes) { - - TypedArray typedArray = context.obtainStyledAttributes( - styleRes, R.styleable.mapbox_LocationComponent); - - LocationComponentOptions.Builder builder = new LocationComponentOptions.Builder() - .enableStaleState(true) - .staleStateTimeout(STALE_STATE_DELAY_MS) - .maxZoomIconScale(MAX_ZOOM_ICON_SCALE_DEFAULT) - .minZoomIconScale(MIN_ZOOM_ICON_SCALE_DEFAULT) - .padding(PADDING_DEFAULT); - - builder.foregroundDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor)) { - builder.foregroundTintColor(typedArray.getColor( - R.styleable.mapbox_LocationComponent_mapbox_foregroundTintColor, -1)); - } - builder.backgroundDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawable, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor)) { - builder.backgroundTintColor(typedArray.getColor( - R.styleable.mapbox_LocationComponent_mapbox_backgroundTintColor, -1)); - } - builder.foregroundDrawableStale(typedArray.getResourceId( - R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawableStale, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor)) { - builder.foregroundStaleTintColor(typedArray.getColor( - R.styleable.mapbox_LocationComponent_mapbox_foregroundStaleTintColor, -1)); - } - builder.backgroundDrawableStale(typedArray.getResourceId( - R.styleable.mapbox_LocationComponent_mapbox_backgroundDrawableStale, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor)) { - builder.backgroundStaleTintColor(typedArray.getColor( - R.styleable.mapbox_LocationComponent_mapbox_backgroundStaleTintColor, -1)); - } - builder.bearingDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationComponent_mapbox_bearingDrawable, -1)); - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor)) { - builder.bearingTintColor(typedArray.getColor( - R.styleable.mapbox_LocationComponent_mapbox_bearingTintColor, -1)); - } - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_enableStaleState)) { - builder.enableStaleState(typedArray.getBoolean( - R.styleable.mapbox_LocationComponent_mapbox_enableStaleState, true)); - } - if (typedArray.hasValue(R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout)) { - builder.staleStateTimeout(typedArray.getInteger( - R.styleable.mapbox_LocationComponent_mapbox_staleStateTimeout, (int) STALE_STATE_DELAY_MS)); - } - builder.gpsDrawable(typedArray.getResourceId( - R.styleable.mapbox_LocationComponent_mapbox_gpsDrawable, -1)); - float elevation = typedArray.getDimension( - R.styleable.mapbox_LocationComponent_mapbox_elevation, 0); - builder.accuracyColor(typedArray.getColor( - R.styleable.mapbox_LocationComponent_mapbox_accuracyColor, -1)); - builder.accuracyAlpha(typedArray.getFloat( - R.styleable.mapbox_LocationComponent_mapbox_accuracyAlpha, ACCURACY_ALPHA_DEFAULT)); - builder.elevation(elevation); - - builder.trackingGesturesManagement(typedArray.getBoolean( - R.styleable.mapbox_LocationComponent_mapbox_trackingGesturesManagement, false)); - builder.trackingInitialMoveThreshold(typedArray.getDimension( - R.styleable.mapbox_LocationComponent_mapbox_trackingInitialMoveThreshold, - context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingInitialMoveThreshold))); - builder.trackingMultiFingerMoveThreshold(typedArray.getDimension( - R.styleable.mapbox_LocationComponent_mapbox_trackingMultiFingerMoveThreshold, - context.getResources().getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold))); - - builder.padding(new int[] { - typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingLeft, 0), - typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingTop, 0), - typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingRight, 0), - typedArray.getInt(R.styleable.mapbox_LocationComponent_mapbox_iconPaddingBottom, 0), - }); - - builder.layerAbove( - typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_above)); - - builder.layerBelow( - typedArray.getString(R.styleable.mapbox_LocationComponent_mapbox_layer_below)); - - float minScale = typedArray.getFloat( - R.styleable.mapbox_LocationComponent_mapbox_minZoomIconScale, MIN_ZOOM_ICON_SCALE_DEFAULT); - float maxScale = typedArray.getFloat( - R.styleable.mapbox_LocationComponent_mapbox_maxZoomIconScale, MAX_ZOOM_ICON_SCALE_DEFAULT); - builder.minZoomIconScale(minScale); - builder.maxZoomIconScale(maxScale); - - float trackingAnimationDurationMultiplier = typedArray.getFloat( - R.styleable.mapbox_LocationComponent_mapbox_trackingAnimationDurationMultiplier, - TRACKING_ANIMATION_DURATION_MULTIPLIER_DEFAULT - ); - builder.trackingAnimationDurationMultiplier(trackingAnimationDurationMultiplier); - - builder.compassAnimationEnabled = typedArray.getBoolean( - R.styleable.mapbox_LocationComponent_mapbox_compassAnimationEnabled, true - ); - - builder.accuracyAnimationEnabled = typedArray.getBoolean( - R.styleable.mapbox_LocationComponent_mapbox_accuracyAnimationEnabled, true - ); - - typedArray.recycle(); - - return builder.build(); - } - - /** - * Takes the currently constructed {@link LocationComponentOptions} object and provides it's builder - * with all the values set matching the values in this instance. This allows you to modify a - * single attribute and then rebuild the object. - * - * @return the builder which contains the values defined in this current instance as defaults. - */ - @NonNull - public Builder toBuilder() { - return new Builder(this); - } - - /** - * Build a new instance of the {@link LocationComponentOptions} class with all the attributes set - * automatically to their defined defaults in this library. This allows you to adjust a few - * attributes while leaving the rest alone and maintaining their default behavior. - * - * @param context your activities context used to acquire the style resource - * @return the builder which contains the default values defined by the style resource - */ - @NonNull - public static Builder builder(@NonNull Context context) { - return LocationComponentOptions.createFromAttributes(context, - R.style.mapbox_LocationComponent).toBuilder(); - } - - /** - * Set the opacity of the accuracy view to a value from 0 to 1, where 0 means the accuracy view is - * completely transparent and 1 means the view is completely opaque. - * References style attribute R.styleable#LocationComponent_accuracyAlpha - * - * @return the opacity of the accuracy view - */ - public float accuracyAlpha() { - return accuracyAlpha; - } - - /** - * Solid color to use as the accuracy view color property. - * References style attribute R.styleable#LocationComponent_accuracyColor - * - * @return the color of the accuracy view - */ - @ColorInt - public int accuracyColor() { - return accuracyColor; - } - - /** - * Defines the drawable used for the stale background icon. - * References style attribute R.styleable#LocationComponent_backgroundDrawableStale - * - * @return the drawable resource ID - */ - @DrawableRes - public int backgroundDrawableStale() { - return backgroundDrawableStale; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will use this image in place of the provided or default mapbox_foregroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String backgroundStaleName() { - return backgroundStaleName; - } - - /** - * Defines the drawable used for the stale foreground icon. - * References style attribute R.styleable#LocationComponent_foregroundDrawableStale - * - * @return the drawable resource ID - */ - @DrawableRes - public int foregroundDrawableStale() { - return foregroundDrawableStale; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_foregroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String foregroundStaleName() { - return foregroundStaleName; - } - - /** - * Defines the drawable used for the navigation state icon. - * References style attribute R.styleable#LocationComponent_gpsDrawable - * - * @return the drawable resource ID - */ - @DrawableRes - public int gpsDrawable() { - return gpsDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_gpsDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String gpsName() { - return gpsName; - } - - /** - * Supply a Drawable that is to be rendered on top of all of the content in the Location LayerComponent layer stack. - * References style attribute R.styleable#LocationComponent_foregroundDrawable - * - * @return the drawable resource used for the foreground layer - */ - @DrawableRes - public int foregroundDrawable() { - return foregroundDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_foregroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String foregroundName() { - return foregroundName; - } - - /** - * Defines the drawable used for the background state icon. - * References style attribute R.styleable#LocationComponent_backgroundDrawable - * - * @return the drawable resource ID - */ - @DrawableRes - public int backgroundDrawable() { - return backgroundDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_backgroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String backgroundName() { - return backgroundName; - } - - /** - * Defines the drawable used for the bearing icon. - * References style attribute R.styleable#LocationComponent_bearingDrawable - * - * @return the drawable resource ID - */ - @DrawableRes - public int bearingDrawable() { - return bearingDrawable; - } - - /** - * String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_bearingDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @return String icon or maki-icon name - */ - @Nullable - public String bearingName() { - return bearingName; - } - - /** - * Defines the bearing icon color as an integer. - * References style attribute R.styleable#LocationComponent_bearingTintColor - * - * @return the color integer resource - */ - @ColorInt - @Nullable - public Integer bearingTintColor() { - return bearingTintColor; - } - - /** - * Defines the foreground color as an integer. - * References style attribute R.styleable#LocationComponent_foregroundTintColor - * - * @return the color integer resource - */ - @ColorInt - @Nullable - public Integer foregroundTintColor() { - return foregroundTintColor; - } - - /** - * Defines the background color as an integer. - * References style attribute R.styleable#LocationComponent_backgroundTintColor - * - * @return the color integer resource - */ - @ColorInt - @Nullable - public Integer backgroundTintColor() { - return backgroundTintColor; - } - - /** - * Defines the foreground stale color as an integer. - * References style attribute R.styleable#LocationComponent_foregroundStaleTintColor - * - * @return the color integer resource - */ - @ColorInt - @Nullable - public Integer foregroundStaleTintColor() { - return foregroundStaleTintColor; - } - - /** - * Defines the background stale color as an integer. - * References style attribute R.styleable#LocationComponent_backgroundStaleTintColor - * - * @return the color integer resource - */ - @ColorInt - @Nullable - public Integer backgroundStaleTintColor() { - return backgroundStaleTintColor; - } - - /** - * Sets the base elevation of this view, in pixels. To turn off the shadow that appears under - * the location icon, set the elevation to 0. - * References style attribute R.styleable#LocationComponent_elevation - * - * @return the elevation currently set for the location component icon - */ - @Dimension - public float elevation() { - return elevation; - } - - /** - * Enable or disable to stale state mode. This mode indicates to the user that the location being - * displayed on the map hasn't been updated in a specific amount of time. - * References style attribute R.styleable#LocationComponent_enableStaleState - * - * @return whether the stale state mode is enabled or not - */ - public boolean enableStaleState() { - return enableStaleState; - } - - /** - * Set the delay before the location icon becomes stale. The timer begins approximately when a new - * location update comes in and using this defined time, if an update hasn't occured by the end, - * the location is considered stale. - * References style attribute R.styleable#LocationComponent_staleStateDelay - * - * @return the duration in milliseconds which it should take before the location is - * considered stale - */ - public long staleStateTimeout() { - return staleStateTimeout; - } - - /** - * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. - *

- *

- * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the - * frame from the viewport. For instance, if the only the top edge is inset, the - * map center is effectively shifted downward. - *

- * - * @return integer array of padding values - */ - @Nullable - @SuppressWarnings("mutable") - public int[] padding() { - return padding; - } - - /** - * The scale factor of the location icon when the map is zoomed in. - * Scaling is linear. - * - * @return icon scale factor - */ - public float maxZoomIconScale() { - return maxZoomIconScale; - } - - /** - * The scale factor of the location icon when the map is zoomed out. - * Scaling is linear. - * - * @return icon scale factor - */ - public float minZoomIconScale() { - return minZoomIconScale; - } - - /** - * Returns whether gesture threshold should be adjusted when camera is in one of the tracking modes. - * This will adjust the focal point and increase thresholds to enable camera manipulation, - * like zooming in and out, without breaking tracking. - *

- * Note: If set to true, this can overwrite some of the gesture thresholds - * and the custom {@link com.mapbox.android.gestures.AndroidGesturesManager} that was set with - * {@link com.mapbox.mapboxsdk.maps.MapboxMap#setGesturesManager(AndroidGesturesManager, boolean, boolean)}. - * - * @return true if gestures are adjusted when in one of the camera tracking modes, false otherwise - * @see Builder#trackingInitialMoveThreshold(float) - * @see Builder#trackingMultiFingerMoveThreshold(float) - */ - public boolean trackingGesturesManagement() { - return trackingGesturesManagement; - } - - /** - * Minimum single pointer movement in pixels required to break camera tracking. - * - * @return the minimum movement - */ - public float trackingInitialMoveThreshold() { - return trackingInitialMoveThreshold; - } - - /** - * Minimum multi pointer movement in pixels required to break camera tracking (for example during scale gesture). - * - * @return the minimum movement - */ - public float trackingMultiFingerMoveThreshold() { - return trackingMultiFingerMoveThreshold; - } - - /** - * Gets the id of the layer that's referenced when placing the component on the map using - * {@link com.mapbox.mapboxsdk.maps.Style#addLayerAbove(Layer, String)}. - *

- * The component is going to placed directly above this layer. - * - * @return layerAbove the id of the layer the component is going to placed directly above. - */ - public String layerAbove() { - return layerAbove; - } - - /** - * Gets the id of the layer that's referenced when placing the component on the map using - * {@link com.mapbox.mapboxsdk.maps.Style#addLayerBelow(Layer, String)}. - *

- * The component is going to placed directly below this layer. - * - * @return layerBelow the id of the layer the component is going to placed directly below. - */ - public String layerBelow() { - return layerBelow; - } - - /** - * Get the tracking animation duration multiplier. - * - * @return tracking animation duration multiplier - */ - public float trackingAnimationDurationMultiplier() { - return trackingAnimationDurationMultiplier; - } - - /** - * Enable or disable smooth animation of compass values for {@link com.mapbox.mapboxsdk.location.modes.CameraMode} - * and {@link com.mapbox.mapboxsdk.location.modes.RenderMode}. - * - * @return whether smooth compass animation is enabled - */ - public boolean compassAnimationEnabled() { - return compassAnimationEnabled; - } - - /** - * Enable or disable smooth animation of the accuracy circle around the user's position. - * - * @return whether smooth animation of the accuracy circle is enabled - */ - public boolean accuracyAnimationEnabled() { - return accuracyAnimationEnabled; - } - - @NonNull - @Override - public String toString() { - return "LocationComponentOptions{" - + "accuracyAlpha=" + accuracyAlpha + ", " - + "accuracyColor=" + accuracyColor + ", " - + "backgroundDrawableStale=" + backgroundDrawableStale + ", " - + "backgroundStaleName=" + backgroundStaleName + ", " - + "foregroundDrawableStale=" + foregroundDrawableStale + ", " - + "foregroundStaleName=" + foregroundStaleName + ", " - + "gpsDrawable=" + gpsDrawable + ", " - + "gpsName=" + gpsName + ", " - + "foregroundDrawable=" + foregroundDrawable + ", " - + "foregroundName=" + foregroundName + ", " - + "backgroundDrawable=" + backgroundDrawable + ", " - + "backgroundName=" + backgroundName + ", " - + "bearingDrawable=" + bearingDrawable + ", " - + "bearingName=" + bearingName + ", " - + "bearingTintColor=" + bearingTintColor + ", " - + "foregroundTintColor=" + foregroundTintColor + ", " - + "backgroundTintColor=" + backgroundTintColor + ", " - + "foregroundStaleTintColor=" + foregroundStaleTintColor + ", " - + "backgroundStaleTintColor=" + backgroundStaleTintColor + ", " - + "elevation=" + elevation + ", " - + "enableStaleState=" + enableStaleState + ", " - + "staleStateTimeout=" + staleStateTimeout + ", " - + "padding=" + Arrays.toString(padding) + ", " - + "maxZoomIconScale=" + maxZoomIconScale + ", " - + "minZoomIconScale=" + minZoomIconScale + ", " - + "trackingGesturesManagement=" + trackingGesturesManagement + ", " - + "trackingInitialMoveThreshold=" + trackingInitialMoveThreshold + ", " - + "trackingMultiFingerMoveThreshold=" + trackingMultiFingerMoveThreshold + ", " - + "layerAbove=" + layerAbove - + "layerBelow=" + layerBelow - + "trackingAnimationDurationMultiplier=" + trackingAnimationDurationMultiplier - + "}"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - LocationComponentOptions options = (LocationComponentOptions) o; - - if (Float.compare(options.accuracyAlpha, accuracyAlpha) != 0) { - return false; - } - if (accuracyColor != options.accuracyColor) { - return false; - } - if (backgroundDrawableStale != options.backgroundDrawableStale) { - return false; - } - if (foregroundDrawableStale != options.foregroundDrawableStale) { - return false; - } - if (gpsDrawable != options.gpsDrawable) { - return false; - } - if (foregroundDrawable != options.foregroundDrawable) { - return false; - } - if (backgroundDrawable != options.backgroundDrawable) { - return false; - } - if (bearingDrawable != options.bearingDrawable) { - return false; - } - if (Float.compare(options.elevation, elevation) != 0) { - return false; - } - if (enableStaleState != options.enableStaleState) { - return false; - } - if (staleStateTimeout != options.staleStateTimeout) { - return false; - } - if (Float.compare(options.maxZoomIconScale, maxZoomIconScale) != 0) { - return false; - } - if (Float.compare(options.minZoomIconScale, minZoomIconScale) != 0) { - return false; - } - if (trackingGesturesManagement != options.trackingGesturesManagement) { - return false; - } - if (Float.compare(options.trackingInitialMoveThreshold, trackingInitialMoveThreshold) != 0) { - return false; - } - if (Float.compare(options.trackingMultiFingerMoveThreshold, trackingMultiFingerMoveThreshold) != 0) { - return false; - } - if (Float.compare(options.trackingAnimationDurationMultiplier, trackingAnimationDurationMultiplier) != 0) { - return false; - } - if (compassAnimationEnabled != options.compassAnimationEnabled) { - return false; - } - if (accuracyAnimationEnabled != options.accuracyAnimationEnabled) { - return false; - } - if (backgroundStaleName != null ? !backgroundStaleName.equals(options.backgroundStaleName) : - options.backgroundStaleName != null) { - return false; - } - if (foregroundStaleName != null ? !foregroundStaleName.equals(options.foregroundStaleName) : - options.foregroundStaleName != null) { - return false; - } - if (gpsName != null ? !gpsName.equals(options.gpsName) : options.gpsName != null) { - return false; - } - if (foregroundName != null ? !foregroundName.equals(options.foregroundName) : options.foregroundName != null) { - return false; - } - if (backgroundName != null ? !backgroundName.equals(options.backgroundName) : options.backgroundName != null) { - return false; - } - if (bearingName != null ? !bearingName.equals(options.bearingName) : options.bearingName != null) { - return false; - } - if (bearingTintColor != null ? !bearingTintColor.equals(options.bearingTintColor) : - options.bearingTintColor != null) { - return false; - } - if (foregroundTintColor != null ? !foregroundTintColor.equals(options.foregroundTintColor) : - options.foregroundTintColor != null) { - return false; - } - if (backgroundTintColor != null ? !backgroundTintColor.equals(options.backgroundTintColor) : - options.backgroundTintColor != null) { - return false; - } - if (foregroundStaleTintColor != null ? !foregroundStaleTintColor.equals(options.foregroundStaleTintColor) : - options.foregroundStaleTintColor != null) { - return false; - } - if (backgroundStaleTintColor != null ? !backgroundStaleTintColor.equals(options.backgroundStaleTintColor) : - options.backgroundStaleTintColor != null) { - return false; - } - if (!Arrays.equals(padding, options.padding)) { - return false; - } - if (layerAbove != null ? !layerAbove.equals(options.layerAbove) : options.layerAbove != null) { - return false; - } - return layerBelow != null ? layerBelow.equals(options.layerBelow) : options.layerBelow == null; - } - - @Override - public int hashCode() { - int result = (accuracyAlpha != +0.0f ? Float.floatToIntBits(accuracyAlpha) : 0); - result = 31 * result + accuracyColor; - result = 31 * result + backgroundDrawableStale; - result = 31 * result + (backgroundStaleName != null ? backgroundStaleName.hashCode() : 0); - result = 31 * result + foregroundDrawableStale; - result = 31 * result + (foregroundStaleName != null ? foregroundStaleName.hashCode() : 0); - result = 31 * result + gpsDrawable; - result = 31 * result + (gpsName != null ? gpsName.hashCode() : 0); - result = 31 * result + foregroundDrawable; - result = 31 * result + (foregroundName != null ? foregroundName.hashCode() : 0); - result = 31 * result + backgroundDrawable; - result = 31 * result + (backgroundName != null ? backgroundName.hashCode() : 0); - result = 31 * result + bearingDrawable; - result = 31 * result + (bearingName != null ? bearingName.hashCode() : 0); - result = 31 * result + (bearingTintColor != null ? bearingTintColor.hashCode() : 0); - result = 31 * result + (foregroundTintColor != null ? foregroundTintColor.hashCode() : 0); - result = 31 * result + (backgroundTintColor != null ? backgroundTintColor.hashCode() : 0); - result = 31 * result + (foregroundStaleTintColor != null ? foregroundStaleTintColor.hashCode() : 0); - result = 31 * result + (backgroundStaleTintColor != null ? backgroundStaleTintColor.hashCode() : 0); - result = 31 * result + (elevation != +0.0f ? Float.floatToIntBits(elevation) : 0); - result = 31 * result + (enableStaleState ? 1 : 0); - result = 31 * result + (int) (staleStateTimeout ^ (staleStateTimeout >>> 32)); - result = 31 * result + Arrays.hashCode(padding); - result = 31 * result + (maxZoomIconScale != +0.0f ? Float.floatToIntBits(maxZoomIconScale) : 0); - result = 31 * result + (minZoomIconScale != +0.0f ? Float.floatToIntBits(minZoomIconScale) : 0); - result = 31 * result + (trackingGesturesManagement ? 1 : 0); - result = 31 * result + (trackingInitialMoveThreshold != +0.0f - ? Float.floatToIntBits(trackingInitialMoveThreshold) : 0); - result = 31 * result + (trackingMultiFingerMoveThreshold != +0.0f - ? Float.floatToIntBits(trackingMultiFingerMoveThreshold) : 0); - result = 31 * result + (layerAbove != null ? layerAbove.hashCode() : 0); - result = 31 * result + (layerBelow != null ? layerBelow.hashCode() : 0); - result = 31 * result + (trackingAnimationDurationMultiplier != +0.0f - ? Float.floatToIntBits(trackingAnimationDurationMultiplier) : 0); - result = 31 * result + (compassAnimationEnabled ? 1 : 0); - result = 31 * result + (accuracyAnimationEnabled ? 1 : 0); - return result; - } - - public static final Parcelable.Creator CREATOR = - new Parcelable.Creator() { - @Override - public LocationComponentOptions createFromParcel(Parcel in) { - return new LocationComponentOptions( - in.readFloat(), - in.readInt(), - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt(), - in.readInt() == 0 ? in.readString() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readInt() == 0 ? in.readInt() : null, - in.readFloat(), - in.readInt() == 1, - in.readLong(), - in.createIntArray(), - in.readFloat(), - in.readFloat(), - in.readInt() == 1, - in.readFloat(), - in.readFloat(), - in.readString(), - in.readString(), - in.readFloat(), - in.readInt() == 1, - in.readInt() == 1 - ); - } - - @Override - public LocationComponentOptions[] newArray(int size) { - return new LocationComponentOptions[size]; - } - }; - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeFloat(accuracyAlpha()); - dest.writeInt(accuracyColor()); - dest.writeInt(backgroundDrawableStale()); - if (backgroundStaleName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(backgroundStaleName()); - } - dest.writeInt(foregroundDrawableStale()); - if (foregroundStaleName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(foregroundStaleName()); - } - dest.writeInt(gpsDrawable()); - if (gpsName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(gpsName()); - } - dest.writeInt(foregroundDrawable()); - if (foregroundName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(foregroundName()); - } - dest.writeInt(backgroundDrawable()); - if (backgroundName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(backgroundName()); - } - dest.writeInt(bearingDrawable()); - if (bearingName() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeString(bearingName()); - } - if (bearingTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(bearingTintColor()); - } - if (foregroundTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(foregroundTintColor()); - } - if (backgroundTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(backgroundTintColor()); - } - if (foregroundStaleTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(foregroundStaleTintColor()); - } - if (backgroundStaleTintColor() == null) { - dest.writeInt(1); - } else { - dest.writeInt(0); - dest.writeInt(backgroundStaleTintColor()); - } - dest.writeFloat(elevation()); - dest.writeInt(enableStaleState() ? 1 : 0); - dest.writeLong(staleStateTimeout()); - dest.writeIntArray(padding()); - dest.writeFloat(maxZoomIconScale()); - dest.writeFloat(minZoomIconScale()); - dest.writeInt(trackingGesturesManagement() ? 1 : 0); - dest.writeFloat(trackingInitialMoveThreshold()); - dest.writeFloat(trackingMultiFingerMoveThreshold()); - dest.writeString(layerAbove()); - dest.writeString(layerBelow()); - dest.writeFloat(trackingAnimationDurationMultiplier); - dest.writeInt(compassAnimationEnabled() ? 1 : 0); - dest.writeInt(accuracyAnimationEnabled() ? 1 : 0); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * Builder class for constructing a new instance of {@link LocationComponentOptions}. - */ - public static class Builder { - - /** - * Build a new instance of this {@link LocationComponentOptions} class. - * - * @return a new instance of {@link LocationComponentOptions} - */ - @NonNull - public LocationComponentOptions build() { - LocationComponentOptions locationComponentOptions = autoBuild(); - if (locationComponentOptions.accuracyAlpha() < 0 || locationComponentOptions.accuracyAlpha() > 1) { - throw new IllegalArgumentException( - "Accuracy alpha value must be between 0.0 and 1.0."); - } - - if (locationComponentOptions.elevation() < 0f) { - throw new IllegalArgumentException("Invalid shadow size " - + locationComponentOptions.elevation() + ". Must be >= 0"); - } - - if (locationComponentOptions.layerAbove() != null && locationComponentOptions.layerBelow() != null) { - throw new IllegalArgumentException("You cannot set both layerAbove and layerBelow options." - + "Choose one or the other."); - } - - return locationComponentOptions; - } - - private Float accuracyAlpha; - private Integer accuracyColor; - private Integer backgroundDrawableStale; - @Nullable - private String backgroundStaleName; - private Integer foregroundDrawableStale; - @Nullable - private String foregroundStaleName; - private Integer gpsDrawable; - @Nullable - private String gpsName; - private Integer foregroundDrawable; - @Nullable - private String foregroundName; - private Integer backgroundDrawable; - @Nullable - private String backgroundName; - private Integer bearingDrawable; - @Nullable - private String bearingName; - @Nullable - private Integer bearingTintColor; - @Nullable - private Integer foregroundTintColor; - @Nullable - private Integer backgroundTintColor; - @Nullable - private Integer foregroundStaleTintColor; - @Nullable - private Integer backgroundStaleTintColor; - private Float elevation; - private Boolean enableStaleState; - private Long staleStateTimeout; - @Nullable - private int[] padding; - private Float maxZoomIconScale; - private Float minZoomIconScale; - private Boolean trackingGesturesManagement; - private Float trackingInitialMoveThreshold; - private Float trackingMultiFingerMoveThreshold; - private String layerAbove; - private String layerBelow; - private Float trackingAnimationDurationMultiplier; - private Boolean compassAnimationEnabled; - private Boolean accuracyAnimationEnabled; - - Builder() { - } - - private Builder(LocationComponentOptions source) { - this.accuracyAlpha = source.accuracyAlpha(); - this.accuracyColor = source.accuracyColor(); - this.backgroundDrawableStale = source.backgroundDrawableStale(); - this.backgroundStaleName = source.backgroundStaleName(); - this.foregroundDrawableStale = source.foregroundDrawableStale(); - this.foregroundStaleName = source.foregroundStaleName(); - this.gpsDrawable = source.gpsDrawable(); - this.gpsName = source.gpsName(); - this.foregroundDrawable = source.foregroundDrawable(); - this.foregroundName = source.foregroundName(); - this.backgroundDrawable = source.backgroundDrawable(); - this.backgroundName = source.backgroundName(); - this.bearingDrawable = source.bearingDrawable(); - this.bearingName = source.bearingName(); - this.bearingTintColor = source.bearingTintColor(); - this.foregroundTintColor = source.foregroundTintColor(); - this.backgroundTintColor = source.backgroundTintColor(); - this.foregroundStaleTintColor = source.foregroundStaleTintColor(); - this.backgroundStaleTintColor = source.backgroundStaleTintColor(); - this.elevation = source.elevation(); - this.enableStaleState = source.enableStaleState(); - this.staleStateTimeout = source.staleStateTimeout(); - this.padding = source.padding(); - this.maxZoomIconScale = source.maxZoomIconScale(); - this.minZoomIconScale = source.minZoomIconScale(); - this.trackingGesturesManagement = source.trackingGesturesManagement(); - this.trackingInitialMoveThreshold = source.trackingInitialMoveThreshold(); - this.trackingMultiFingerMoveThreshold = source.trackingMultiFingerMoveThreshold(); - this.layerAbove = source.layerAbove(); - this.layerBelow = source.layerBelow(); - this.trackingAnimationDurationMultiplier = source.trackingAnimationDurationMultiplier(); - this.compassAnimationEnabled = source.compassAnimationEnabled(); - this.accuracyAnimationEnabled = source.accuracyAnimationEnabled(); - } - - /** - * Set the opacity of the accuracy view to a value from 0 to 1, where 0 means the accuracy view - * is completely transparent and 1 means the view is completely opaque. - * References style attribute R.styleable#LocationComponent_accuracyAlpha - * - * @param accuracyAlpha the opacity of the accuracy view - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder accuracyAlpha(float accuracyAlpha) { - this.accuracyAlpha = accuracyAlpha; - return this; - } - - /** - * Solid color to use as the accuracy view color property. - * References style attribute R.styleable#LocationComponent_accuracyColor - * - * @param accuracyColor the color of the accuracy view - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder accuracyColor(int accuracyColor) { - this.accuracyColor = accuracyColor; - return this; - } - - /** - * Defines the drawable used for the stale background icon. - * References style attribute R.styleable#LocationComponent_backgroundDrawableStale - * - * @param backgroundDrawableStale the drawable resource ID - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder backgroundDrawableStale(int backgroundDrawableStale) { - this.backgroundDrawableStale = backgroundDrawableStale; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_backgroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param backgroundStaleName String icon or maki-icon name - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder backgroundStaleName(@Nullable String backgroundStaleName) { - this.backgroundStaleName = backgroundStaleName; - return this; - } - - /** - * Defines the drawable used for the stale foreground icon. - * References style attribute R.styleable#LocationComponent_foregroundDrawableStale - * - * @param foregroundDrawableStale the drawable resource ID - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder foregroundDrawableStale(int foregroundDrawableStale) { - this.foregroundDrawableStale = foregroundDrawableStale; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_foregroundDrawableStale. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param foregroundStaleName String icon or maki-icon name - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder foregroundStaleName(@Nullable String foregroundStaleName) { - this.foregroundStaleName = foregroundStaleName; - return this; - } - - /** - * Defines the drawable used for the navigation state icon. - * References style attribute R.styleable#LocationComponent_gpsDrawable - * - * @param gpsDrawable the drawable resource ID - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder gpsDrawable(int gpsDrawable) { - this.gpsDrawable = gpsDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_gpsDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param gpsName String icon or maki-icon name - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder gpsName(@Nullable String gpsName) { - this.gpsName = gpsName; - return this; - } - - /** - * Supply a Drawable that is to be rendered on top of all of the content in the Location Component layer stack. - * References style attribute R.styleable#LocationComponent_foregroundDrawable - * - * @param foregroundDrawable the drawable resource used for the foreground layer - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder foregroundDrawable(int foregroundDrawable) { - this.foregroundDrawable = foregroundDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_foregroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param foregroundName String icon or maki-icon name - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder foregroundName(@Nullable String foregroundName) { - this.foregroundName = foregroundName; - return this; - } - - /** - * Defines the drawable used for the background state icon. - * References style attribute R.styleable#LocationComponent_backgroundDrawable - * - * @param backgroundDrawable the drawable resource ID - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder backgroundDrawable(int backgroundDrawable) { - this.backgroundDrawable = backgroundDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_backgroundDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param backgroundName String icon or maki-icon name - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder backgroundName(@Nullable String backgroundName) { - this.backgroundName = backgroundName; - return this; - } - - /** - * Defines the drawable used for the bearing icon. - * References style attribute R.styleable#LocationComponent_bearingDrawable - * - * @param bearingDrawable the drawable resource ID - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder bearingDrawable(int bearingDrawable) { - this.bearingDrawable = bearingDrawable; - return this; - } - - /** - * Given a String image name, identical to one used in - * the first parameter of {@link com.mapbox.mapboxsdk.maps.Style.Builder#addImage(String, Bitmap)}, the - * component, will used this image in place of the provided or default mapbox_bearingDrawable. - *

- * A maki-icon name (example: "circle-15") may also be provided. These are images that can be loaded - * with certain styles. Note, this will fail if the provided icon name is not provided by the loaded map style. - *

- * - * @param bearingName String icon or maki-icon name - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder bearingName(@Nullable String bearingName) { - this.bearingName = bearingName; - return this; - } - - /** - * Defines the bearing icon color as an integer. - * References style attribute R.styleable#LocationComponent_bearingTintColor - * - * @param bearingTintColor the color integer resource - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder bearingTintColor(@Nullable Integer bearingTintColor) { - this.bearingTintColor = bearingTintColor; - return this; - } - - /** - * Defines the foreground color as an integer. - * References style attribute R.styleable#LocationComponent_foregroundTintColor - * - * @param foregroundTintColor the color integer resource - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder foregroundTintColor(@Nullable Integer foregroundTintColor) { - this.foregroundTintColor = foregroundTintColor; - return this; - } - - /** - * Defines the background color as an integer. - * References style attribute R.styleable#LocationComponent_backgroundTintColor - * - * @param backgroundTintColor the color integer resource - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder backgroundTintColor(@Nullable Integer backgroundTintColor) { - this.backgroundTintColor = backgroundTintColor; - return this; - } - - /** - * Defines the foreground stale color as an integer. - * References style attribute R.styleable#LocationComponent_foregroundStaleTintColor - * - * @param foregroundStaleTintColor the color integer resource - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder foregroundStaleTintColor(@Nullable Integer foregroundStaleTintColor) { - this.foregroundStaleTintColor = foregroundStaleTintColor; - return this; - } - - /** - * Defines the background stale color as an integer. - * References style attribute R.styleable#LocationComponent_backgroundStaleTintColor - * - * @param backgroundStaleTintColor the color integer resource - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder backgroundStaleTintColor(@Nullable Integer backgroundStaleTintColor) { - this.backgroundStaleTintColor = backgroundStaleTintColor; - return this; - } - - /** - * Sets the base elevation of this view, in pixels. - * References style attribute R.styleable#LocationComponent_elevation - * - * @param elevation the elevation currently set for the location icon - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder elevation(float elevation) { - this.elevation = elevation; - return this; - } - - /** - * Enable or disable to stale state mode. This mode indicates to the user that the location - * being displayed on the map hasn't been updated in a specific amount of time. - * References style attribute R.styleable#LocationComponent_enableStaleState - * - * @param enabled whether the stale state mode is enabled or not - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder enableStaleState(boolean enabled) { - this.enableStaleState = enabled; - return this; - } - - /** - * Set the timeout before the location icon becomes stale. The timer begins approximately when a - * new location update comes in and using this defined time, if an update hasn't occurred by the - * end, the location is considered stale. - * References style attribute R.styleable#LocationComponent_staleStateTimeout - * - * @param timeout the duration in milliseconds which it should take before the location is - * considered stale - * @return this builder for chaining options together - */ - @NonNull - public LocationComponentOptions.Builder staleStateTimeout(long timeout) { - this.staleStateTimeout = timeout; - return this; - } - - /** - * Sets the distance from the edges of the map view’s frame to the edges of the map - * view’s logical viewport. - *

- *

- * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as `centerCoordinate` assume a viewport that matches the map - * view’s frame. Otherwise, those properties are inset, excluding part of the - * frame from the viewport. For instance, if the only the top edge is inset, the - * map center is effectively shifted downward. - *

- * - * @param padding The margins for the map in pixels (left, top, right, bottom). - * @deprecated Use {@link CameraPosition.Builder#padding(double, double, double, double)} - * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)} instead. - */ - @NonNull - @Deprecated - public LocationComponentOptions.Builder padding(@Nullable int[] padding) { - if (padding == null) { - throw new NullPointerException("Null padding"); - } - this.padding = padding; - return this; - } - - /** - * Sets the scale factor of the location icon when the map is zoomed in. - * Scaling is linear and the new pixel size of the image will be the original pixel size multiplied by the argument. - *

- * Set both this and {@link #minZoomIconScale(float)} to 1f to disable location icon scaling. - *

- * Scaling is based on the maps minimum and maximum zoom levels in time of component's style application. - * - * @param maxZoomIconScale icon scale factor - */ - @NonNull - public LocationComponentOptions.Builder maxZoomIconScale(float maxZoomIconScale) { - this.maxZoomIconScale = maxZoomIconScale; - return this; - } - - /** - * Sets the scale factor of the location icon when the map is zoomed out. - * Scaling is linear and the new pixel size of the image will be the original pixel size multiplied by the argument. - *

- * Set both this and {@link #maxZoomIconScale(float)} to 1f to disable location icon scaling. - *

- * Scaling is based on the maps minimum and maximum zoom levels in time of component's style application. - * - * @param minZoomIconScale icon scale factor - */ - @NonNull - public LocationComponentOptions.Builder minZoomIconScale(float minZoomIconScale) { - this.minZoomIconScale = minZoomIconScale; - return this; - } - - /** - * Set whether gesture threshold should be adjusted when camera is in one of the tracking modes. - * This will adjust the focal point and increase thresholds to enable camera manipulation, - * like zooming in and out, without breaking tracking. - *

- * Note: This can overwrite some of the gesture thresholds - * and the custom {@link com.mapbox.android.gestures.AndroidGesturesManager} that was set with - * {@link com.mapbox.mapboxsdk.maps.MapboxMap#setGesturesManager(AndroidGesturesManager, boolean, boolean)}. - * - * @param trackingGesturesManagement true if gestures should be adjusted when in one of the camera tracking modes, - * false otherwise - * @see Builder#trackingInitialMoveThreshold(float) - * @see Builder#trackingMultiFingerMoveThreshold(float) - */ - @NonNull - public LocationComponentOptions.Builder trackingGesturesManagement(boolean trackingGesturesManagement) { - this.trackingGesturesManagement = trackingGesturesManagement; - return this; - } - - /** - * Sets minimum single pointer movement (map pan) in pixels required to break camera tracking. - * - * @param moveThreshold the minimum movement - */ - @NonNull - public LocationComponentOptions.Builder trackingInitialMoveThreshold(float moveThreshold) { - this.trackingInitialMoveThreshold = moveThreshold; - return this; - } - - /** - * Sets minimum multi pointer movement (map pan) in pixels required to break camera tracking - * (for example during scale gesture). - * - * @param moveThreshold the minimum movement - */ - @NonNull - public LocationComponentOptions.Builder trackingMultiFingerMoveThreshold(float moveThreshold) { - this.trackingMultiFingerMoveThreshold = moveThreshold; - return this; - } - - /** - * Sets the id of the layer that's referenced when placing the component on the map using - * {@link com.mapbox.mapboxsdk.maps.Style#addLayerAbove(Layer, String)}. - *

- * The component is going to placed directly above this layer. - * - * @param layerAbove the id of the layer the component is going to placed directly above. - */ - @NonNull - public LocationComponentOptions.Builder layerAbove(String layerAbove) { - this.layerAbove = layerAbove; - return this; - } - - /** - * Sets the id of the layer that's referenced when placing the component on the map using - * {@link com.mapbox.mapboxsdk.maps.Style#addLayerBelow(Layer, String)}. - *

- * The component is going to placed directly below this layer. - * - * @param layerBelow the id of the layer the component is going to placed directly below. - */ - @NonNull - public LocationComponentOptions.Builder layerBelow(String layerBelow) { - this.layerBelow = layerBelow; - return this; - } - - /** - * Sets the tracking animation duration multiplier. - * - * @param trackingAnimationDurationMultiplier the tracking animation duration multiplier - */ - @NonNull - public LocationComponentOptions.Builder trackingAnimationDurationMultiplier( - float trackingAnimationDurationMultiplier) { - this.trackingAnimationDurationMultiplier = trackingAnimationDurationMultiplier; - return this; - } - - /** - * Enable or disable smooth animation of compass values for {@link com.mapbox.mapboxsdk.location.modes.CameraMode} - * and {@link com.mapbox.mapboxsdk.location.modes.RenderMode}. - * - * @return whether smooth compass animation is enabled - */ - public LocationComponentOptions.Builder compassAnimationEnabled(Boolean compassAnimationEnabled) { - this.compassAnimationEnabled = compassAnimationEnabled; - return this; - } - - /** - * Enable or disable smooth animation of the accuracy circle around the user's position. - * - * @return whether smooth animation of the accuracy circle is enabled - */ - public Builder accuracyAnimationEnabled(Boolean accuracyAnimationEnabled) { - this.accuracyAnimationEnabled = accuracyAnimationEnabled; - return this; - } - - @Nullable - LocationComponentOptions autoBuild() { - String missing = ""; - if (this.accuracyAlpha == null) { - missing += " accuracyAlpha"; - } - if (this.accuracyColor == null) { - missing += " accuracyColor"; - } - if (this.backgroundDrawableStale == null) { - missing += " backgroundDrawableStale"; - } - if (this.foregroundDrawableStale == null) { - missing += " foregroundDrawableStale"; - } - if (this.gpsDrawable == null) { - missing += " gpsDrawable"; - } - if (this.foregroundDrawable == null) { - missing += " foregroundDrawable"; - } - if (this.backgroundDrawable == null) { - missing += " backgroundDrawable"; - } - if (this.bearingDrawable == null) { - missing += " bearingDrawable"; - } - if (this.elevation == null) { - missing += " elevation"; - } - if (this.enableStaleState == null) { - missing += " enableStaleState"; - } - if (this.staleStateTimeout == null) { - missing += " staleStateTimeout"; - } - if (this.padding == null) { - missing += " padding"; - } - if (this.maxZoomIconScale == null) { - missing += " maxZoomIconScale"; - } - if (this.minZoomIconScale == null) { - missing += " minZoomIconScale"; - } - if (this.trackingGesturesManagement == null) { - missing += " trackingGesturesManagement"; - } - if (this.trackingInitialMoveThreshold == null) { - missing += " trackingInitialMoveThreshold"; - } - if (this.trackingMultiFingerMoveThreshold == null) { - missing += " trackingMultiFingerMoveThreshold"; - } - if (this.trackingAnimationDurationMultiplier == null) { - missing += " trackingAnimationDurationMultiplier"; - } - if (!missing.isEmpty()) { - throw new IllegalStateException("Missing required properties:" + missing); - } - return new LocationComponentOptions( - this.accuracyAlpha, - this.accuracyColor, - this.backgroundDrawableStale, - this.backgroundStaleName, - this.foregroundDrawableStale, - this.foregroundStaleName, - this.gpsDrawable, - this.gpsName, - this.foregroundDrawable, - this.foregroundName, - this.backgroundDrawable, - this.backgroundName, - this.bearingDrawable, - this.bearingName, - this.bearingTintColor, - this.foregroundTintColor, - this.backgroundTintColor, - this.foregroundStaleTintColor, - this.backgroundStaleTintColor, - this.elevation, - this.enableStaleState, - this.staleStateTimeout, - this.padding, - this.maxZoomIconScale, - this.minZoomIconScale, - trackingGesturesManagement, - this.trackingInitialMoveThreshold, - this.trackingMultiFingerMoveThreshold, - this.layerAbove, - this.layerBelow, - this.trackingAnimationDurationMultiplier, - this.compassAnimationEnabled, - this.accuracyAnimationEnabled); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManager.java deleted file mode 100644 index f2e4d0bb4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManager.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.Style; -import com.mapbox.mapboxsdk.style.layers.Layer; - -class LocationComponentPositionManager { - - @NonNull - private final Style style; - - @Nullable - private String layerAbove; - - @Nullable - private String layerBelow; - - LocationComponentPositionManager(@NonNull Style style, @Nullable String layerAbove, @Nullable String layerBelow) { - this.style = style; - this.layerAbove = layerAbove; - this.layerBelow = layerBelow; - } - - /** - * Returns true whenever layer above/below configuration has changed and requires re-layout. - */ - boolean update(@Nullable String layerAbove, @Nullable String layerBelow) { - boolean requiresUpdate = - !(this.layerAbove == layerAbove || (this.layerAbove != null && this.layerAbove.equals(layerAbove))) - || !(this.layerBelow == layerBelow || (this.layerBelow != null && this.layerBelow.equals(layerBelow))); - - this.layerAbove = layerAbove; - this.layerBelow = layerBelow; - return requiresUpdate; - } - - void addLayerToMap(@NonNull Layer layer) { - if (layerAbove != null) { - style.addLayerAbove(layer, layerAbove); - } else if (layerBelow != null) { - style.addLayerBelow(layer, layerBelow); - } else { - style.addLayer(layer); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java deleted file mode 100644 index 54f8ee6d1..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/LocationLayerController.java +++ /dev/null @@ -1,464 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.support.annotation.ColorInt; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; - -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.location.modes.RenderMode; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Style; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.SymbolLayer; -import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; - -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_ALPHA; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_COLOR; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_BEARING_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_LOCATION_STALE; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER; -import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; -import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; -import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; -import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; -import static com.mapbox.mapboxsdk.style.layers.Property.NONE; -import static com.mapbox.mapboxsdk.style.layers.Property.VISIBLE; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; -import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.visibility; -import static com.mapbox.mapboxsdk.utils.ColorUtils.colorToRgbaString; - -final class LocationLayerController { - - @RenderMode.Mode - private int renderMode; - - private final MapboxMap mapboxMap; - private Style style; - private final LayerSourceProvider layerSourceProvider; - private final LayerBitmapProvider bitmapProvider; - private LocationComponentOptions options; - private final OnRenderModeChangedListener internalRenderModeChangedListener; - - @VisibleForTesting - final Set layerSet = new HashSet<>(); - private Feature locationFeature; - private GeoJsonSource locationSource; - - private boolean isHidden = true; - - private LocationComponentPositionManager positionManager; - - LocationLayerController(MapboxMap mapboxMap, Style style, LayerSourceProvider layerSourceProvider, - LayerFeatureProvider featureProvider, LayerBitmapProvider bitmapProvider, - @NonNull LocationComponentOptions options, - @NonNull OnRenderModeChangedListener internalRenderModeChangedListener) { - this.mapboxMap = mapboxMap; - this.style = style; - this.layerSourceProvider = layerSourceProvider; - this.bitmapProvider = bitmapProvider; - this.locationFeature = featureProvider.generateLocationFeature(locationFeature, options); - this.internalRenderModeChangedListener = internalRenderModeChangedListener; - initializeComponents(style, options); - } - - void initializeComponents(Style style, LocationComponentOptions options) { - this.style = style; - this.positionManager = new LocationComponentPositionManager(style, options.layerAbove(), options.layerBelow()); - addLocationSource(); - addLayers(); - applyStyle(options); - - if (isHidden) { - hide(); - } else { - show(); - } - } - - void applyStyle(@NonNull LocationComponentOptions options) { - if (positionManager.update(options.layerAbove(), options.layerBelow())) { - removeLayers(); - addLayers(); - if (isHidden) { - hide(); - } - } - - this.options = options; - - float elevation = options.elevation(); - // Only add icon elevation if the values greater than 0. - if (elevation > 0) { - styleShadow(options); - } - styleForeground(options); - styleBackground(options); - styleBearing(options); - styleAccuracy(options.accuracyAlpha(), options.accuracyColor()); - styleScaling(options); - determineIconsSource(options); - - if (!isHidden) { - show(); - } - } - - void setRenderMode(@RenderMode.Mode int renderMode) { - if (this.renderMode == renderMode) { - return; - } - this.renderMode = renderMode; - - styleForeground(options); - determineIconsSource(options); - if (!isHidden) { - show(); - } - internalRenderModeChangedListener.onRenderModeChanged(renderMode); - } - - int getRenderMode() { - return renderMode; - } - - // - // Layer action - // - - void show() { - isHidden = false; - boolean isStale = locationFeature.getBooleanProperty(PROPERTY_LOCATION_STALE); - switch (renderMode) { - case RenderMode.NORMAL: - setLayerVisibility(SHADOW_LAYER, true); - setLayerVisibility(FOREGROUND_LAYER, true); - setLayerVisibility(BACKGROUND_LAYER, true); - setLayerVisibility(ACCURACY_LAYER, !isStale); - setLayerVisibility(BEARING_LAYER, false); - break; - case RenderMode.COMPASS: - setLayerVisibility(SHADOW_LAYER, true); - setLayerVisibility(FOREGROUND_LAYER, true); - setLayerVisibility(BACKGROUND_LAYER, true); - setLayerVisibility(ACCURACY_LAYER, !isStale); - setLayerVisibility(BEARING_LAYER, true); - break; - case RenderMode.GPS: - setLayerVisibility(SHADOW_LAYER, false); - setLayerVisibility(FOREGROUND_LAYER, true); - setLayerVisibility(BACKGROUND_LAYER, true); - setLayerVisibility(ACCURACY_LAYER, false); - setLayerVisibility(BEARING_LAYER, false); - break; - default: - break; - } - } - - void hide() { - isHidden = true; - for (String layerId : layerSet) { - setLayerVisibility(layerId, false); - } - } - - void updateForegroundOffset(double tilt) { - JsonArray foregroundJsonArray = new JsonArray(); - foregroundJsonArray.add(0f); - foregroundJsonArray.add((float) (-0.05 * tilt)); - locationFeature.addProperty(PROPERTY_FOREGROUND_ICON_OFFSET, foregroundJsonArray); - - JsonArray backgroundJsonArray = new JsonArray(); - backgroundJsonArray.add(0f); - backgroundJsonArray.add((float) (0.05 * tilt)); - locationFeature.addProperty(PROPERTY_SHADOW_ICON_OFFSET, backgroundJsonArray); - - refreshSource(); - } - - void updateForegroundBearing(float bearing) { - if (renderMode != RenderMode.GPS) { - setBearingProperty(PROPERTY_GPS_BEARING, bearing); - } - } - - boolean isHidden() { - return isHidden; - } - - boolean isConsumingCompass() { - return renderMode == RenderMode.COMPASS; - } - - private void setLayerVisibility(@NonNull String layerId, boolean visible) { - Layer layer = style.getLayer(layerId); - if (layer != null) { - String targetVisibility = visible ? VISIBLE : NONE; - if (!layer.getVisibility().value.equals(targetVisibility)) { - layer.setProperties(visibility(visible ? VISIBLE : NONE)); - } - } - } - - private void addLayers() { - // positions the top-most reference layer - Layer layer = layerSourceProvider.generateLayer(BEARING_LAYER); - positionManager.addLayerToMap(layer); - layerSet.add(layer.getId()); - - // adds remaining layers while keeping the order - addSymbolLayer(FOREGROUND_LAYER, BEARING_LAYER); - addSymbolLayer(BACKGROUND_LAYER, FOREGROUND_LAYER); - addSymbolLayer(SHADOW_LAYER, BACKGROUND_LAYER); - addAccuracyLayer(); - } - - private void addSymbolLayer(@NonNull String layerId, @NonNull String beforeLayerId) { - Layer layer = layerSourceProvider.generateLayer(layerId); - addLayerToMap(layer, beforeLayerId); - } - - private void addAccuracyLayer() { - Layer accuracyLayer = layerSourceProvider.generateAccuracyLayer(); - addLayerToMap(accuracyLayer, BACKGROUND_LAYER); - } - - private void addLayerToMap(Layer layer, @NonNull String idBelowLayer) { - style.addLayerBelow(layer, idBelowLayer); - layerSet.add(layer.getId()); - } - - private void removeLayers() { - for (String layerId : layerSet) { - style.removeLayer(layerId); - } - layerSet.clear(); - } - - private void setBearingProperty(@NonNull String propertyId, float bearing) { - locationFeature.addNumberProperty(propertyId, bearing); - refreshSource(); - } - - private void updateAccuracyRadius(float accuracy) { - locationFeature.addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracy); - refreshSource(); - } - - // - // Source actions - // - - private void addLocationSource() { - locationSource = layerSourceProvider.generateSource(locationFeature); - style.addSource(locationSource); - } - - private void refreshSource() { - GeoJsonSource source = style.getSourceAs(LOCATION_SOURCE); - if (source != null) { - locationSource.setGeoJson(locationFeature); - } - } - - private void setLocationPoint(Point locationPoint) { - JsonObject properties = locationFeature.properties(); - if (properties != null) { - locationFeature = Feature.fromGeometry(locationPoint, properties); - refreshSource(); - } - } - - // - // Styling - // - - private void styleBackground(LocationComponentOptions options) { - Bitmap backgroundBitmap = bitmapProvider.generateBitmap( - options.backgroundDrawable(), options.backgroundTintColor() - ); - Bitmap backgroundStaleBitmap = bitmapProvider.generateBitmap( - options.backgroundDrawableStale(), options.backgroundStaleTintColor() - ); - style.addImage(BACKGROUND_ICON, backgroundBitmap); - style.addImage(BACKGROUND_STALE_ICON, backgroundStaleBitmap); - } - - private void styleShadow(@NonNull LocationComponentOptions options) { - style.addImage(SHADOW_ICON, bitmapProvider.generateShadowBitmap(options)); - } - - private void styleBearing(LocationComponentOptions options) { - Bitmap bearingBitmap = bitmapProvider.generateBitmap(options.bearingDrawable(), options.bearingTintColor()); - style.addImage(BEARING_ICON, bearingBitmap); - } - - private void styleAccuracy(float accuracyAlpha, @ColorInt int accuracyColor) { - locationFeature.addNumberProperty(PROPERTY_ACCURACY_ALPHA, accuracyAlpha); - locationFeature.addStringProperty(PROPERTY_ACCURACY_COLOR, colorToRgbaString(accuracyColor)); - refreshSource(); - } - - private void styleForeground(LocationComponentOptions options) { - Bitmap foregroundBitmap = bitmapProvider.generateBitmap( - options.foregroundDrawable(), options.foregroundTintColor() - ); - Bitmap foregroundBitmapStale = bitmapProvider.generateBitmap( - options.foregroundDrawableStale(), options.foregroundStaleTintColor() - ); - if (renderMode == RenderMode.GPS) { - foregroundBitmap = bitmapProvider.generateBitmap( - options.gpsDrawable(), options.foregroundTintColor() - ); - foregroundBitmapStale = bitmapProvider.generateBitmap( - options.gpsDrawable(), options.foregroundStaleTintColor() - ); - } - style.addImage(FOREGROUND_ICON, foregroundBitmap); - style.addImage(FOREGROUND_STALE_ICON, foregroundBitmapStale); - } - - private void styleScaling(@NonNull LocationComponentOptions options) { - for (String layerId : layerSet) { - Layer layer = style.getLayer(layerId); - if (layer instanceof SymbolLayer) { - layer.setProperties( - iconSize( - interpolate(linear(), zoom(), - stop(mapboxMap.getMinZoomLevel(), options.minZoomIconScale()), - stop(mapboxMap.getMaxZoomLevel(), options.maxZoomIconScale()) - ) - ) - ); - } - } - } - - private void determineIconsSource(LocationComponentOptions options) { - String foregroundIconString = buildIconString( - renderMode == RenderMode.GPS ? options.gpsName() : options.foregroundName(), FOREGROUND_ICON); - String foregroundStaleIconString = buildIconString(options.foregroundStaleName(), FOREGROUND_STALE_ICON); - String backgroundIconString = buildIconString(options.backgroundName(), BACKGROUND_ICON); - String backgroundStaleIconString = buildIconString(options.backgroundStaleName(), BACKGROUND_STALE_ICON); - String bearingIconString = buildIconString(options.bearingName(), BEARING_ICON); - - locationFeature.addStringProperty(PROPERTY_FOREGROUND_ICON, foregroundIconString); - locationFeature.addStringProperty(PROPERTY_BACKGROUND_ICON, backgroundIconString); - locationFeature.addStringProperty(PROPERTY_FOREGROUND_STALE_ICON, foregroundStaleIconString); - locationFeature.addStringProperty(PROPERTY_BACKGROUND_STALE_ICON, backgroundStaleIconString); - locationFeature.addStringProperty(PROPERTY_BEARING_ICON, bearingIconString); - refreshSource(); - } - - @NonNull - private String buildIconString(@Nullable String bitmapName, @NonNull String drawableName) { - if (bitmapName != null) { - return bitmapName; - } - return drawableName; - } - - void setLocationsStale(boolean isStale) { - locationFeature.addBooleanProperty(PROPERTY_LOCATION_STALE, isStale); - refreshSource(); - if (renderMode != RenderMode.GPS) { - setLayerVisibility(ACCURACY_LAYER, !isStale); - } - } - - // - // Map click event - // - - boolean onMapClick(@NonNull LatLng point) { - PointF screenLoc = mapboxMap.getProjection().toScreenLocation(point); - List features = mapboxMap.queryRenderedFeatures(screenLoc, - BACKGROUND_LAYER, - FOREGROUND_LAYER, - BEARING_LAYER - ); - return !features.isEmpty(); - } - - private final MapboxAnimator.AnimationsValueChangeListener latLngValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(LatLng value) { - Point point = Point.fromLngLat(value.getLongitude(), value.getLatitude()); - setLocationPoint(point); - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener gpsBearingValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - setBearingProperty(PROPERTY_GPS_BEARING, value); - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener compassBearingValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - setBearingProperty(PROPERTY_COMPASS_BEARING, value); - } - }; - - private final MapboxAnimator.AnimationsValueChangeListener accuracyValueListener = - new MapboxAnimator.AnimationsValueChangeListener() { - @Override - public void onNewAnimationValue(Float value) { - updateAccuracyRadius(value); - } - }; - - Set getAnimationListeners() { - Set holders = new HashSet<>(); - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_LATLNG, latLngValueListener)); - - if (renderMode == RenderMode.GPS) { - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING, gpsBearingValueListener)); - } else if (renderMode == RenderMode.COMPASS) { - holders.add( - new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING, compassBearingValueListener)); - } - - if (renderMode == RenderMode.COMPASS || renderMode == RenderMode.NORMAL) { - holders.add(new AnimatorListenerHolder(MapboxAnimator.ANIMATOR_LAYER_ACCURACY, accuracyValueListener)); - } - - return holders; - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java deleted file mode 100644 index dff7369cd..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimator.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.TypeEvaluator; -import android.animation.ValueAnimator; -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Abstract class for all of the location component animators. - * - * @param Data type that will be animated. - */ -abstract class MapboxAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener { - @Retention(RetentionPolicy.SOURCE) - @IntDef( { - ANIMATOR_LAYER_LATLNG, - ANIMATOR_CAMERA_LATLNG, - ANIMATOR_LAYER_GPS_BEARING, - ANIMATOR_LAYER_COMPASS_BEARING, - ANIMATOR_CAMERA_GPS_BEARING, - ANIMATOR_CAMERA_COMPASS_BEARING, - ANIMATOR_LAYER_ACCURACY, - ANIMATOR_ZOOM, - ANIMATOR_TILT - }) - @interface Type { - } - - static final int ANIMATOR_LAYER_LATLNG = 0; - static final int ANIMATOR_CAMERA_LATLNG = 1; - static final int ANIMATOR_LAYER_GPS_BEARING = 2; - static final int ANIMATOR_LAYER_COMPASS_BEARING = 3; - static final int ANIMATOR_CAMERA_GPS_BEARING = 4; - static final int ANIMATOR_CAMERA_COMPASS_BEARING = 5; - static final int ANIMATOR_LAYER_ACCURACY = 6; - static final int ANIMATOR_ZOOM = 7; - static final int ANIMATOR_TILT = 8; - - private final AnimationsValueChangeListener updateListener; - private final K target; - private K animatedValue; - - private final double minUpdateInterval; - private long timeElapsed; - - MapboxAnimator(@NonNull K previous, @NonNull K target, @NonNull AnimationsValueChangeListener updateListener, - int maxAnimationFps) { - minUpdateInterval = 1E9 / maxAnimationFps; - setObjectValues(previous, target); - setEvaluator(provideEvaluator()); - this.updateListener = updateListener; - this.target = target; - addUpdateListener(this); - addListener(new AnimatorListener()); - } - - @Override - public void onAnimationUpdate(ValueAnimator animation) { - animatedValue = (K) animation.getAnimatedValue(); - - long currentTime = System.nanoTime(); - if ((currentTime - timeElapsed) < minUpdateInterval) { - return; - } - - postUpdates(); - timeElapsed = currentTime; - } - - private class AnimatorListener extends AnimatorListenerAdapter { - @Override - public void onAnimationEnd(Animator animation) { - postUpdates(); - } - } - - private void postUpdates() { - updateListener.onNewAnimationValue(animatedValue); - } - - K getTarget() { - return target; - } - - abstract TypeEvaluator provideEvaluator(); - - interface AnimationsValueChangeListener { - void onNewAnimationValue(K value); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java deleted file mode 100644 index 938f4ec74..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorProvider.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -final class MapboxAnimatorProvider { - - private static MapboxAnimatorProvider INSTANCE; - - private MapboxAnimatorProvider() { - // private constructor - } - - public static MapboxAnimatorProvider getInstance() { - if (INSTANCE == null) { - INSTANCE = new MapboxAnimatorProvider(); - } - return INSTANCE; - } - - MapboxLatLngAnimator latLngAnimator(LatLng previous, LatLng target, - MapboxAnimator.AnimationsValueChangeListener updateListener, - int maxAnimationFps) { - return new MapboxLatLngAnimator(previous, target, updateListener, maxAnimationFps); - } - - MapboxFloatAnimator floatAnimator(Float previous, Float target, - MapboxAnimator.AnimationsValueChangeListener updateListener, - int maxAnimationFps) { - return new MapboxFloatAnimator(previous, target, updateListener, maxAnimationFps); - } - - MapboxCameraAnimatorAdapter cameraAnimator(Float previous, Float target, - MapboxAnimator.AnimationsValueChangeListener updateListener, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - return new MapboxCameraAnimatorAdapter(previous, target, updateListener, cancelableCallback); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java deleted file mode 100644 index 1d09f8ae7..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxAnimatorSetProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.support.annotation.NonNull; -import android.view.animation.Interpolator; - -import java.util.List; - -class MapboxAnimatorSetProvider { - private static MapboxAnimatorSetProvider instance; - - private MapboxAnimatorSetProvider() { - // private constructor - } - - static MapboxAnimatorSetProvider getInstance() { - if (instance == null) { - instance = new MapboxAnimatorSetProvider(); - } - return instance; - } - - void startAnimation(@NonNull List animators, @NonNull Interpolator interpolator, - long duration) { - AnimatorSet locationAnimatorSet = new AnimatorSet(); - locationAnimatorSet.playTogether(animators); - locationAnimatorSet.setInterpolator(interpolator); - locationAnimatorSet.setDuration(duration); - locationAnimatorSet.start(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java deleted file mode 100644 index 24bb82a2d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxCameraAnimatorAdapter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.maps.MapboxMap; - -class MapboxCameraAnimatorAdapter extends MapboxFloatAnimator { - @Nullable - private final MapboxMap.CancelableCallback cancelableCallback; - - MapboxCameraAnimatorAdapter(Float previous, Float target, - AnimationsValueChangeListener updateListener, - @Nullable MapboxMap.CancelableCallback cancelableCallback) { - super(previous, target, updateListener, Integer.MAX_VALUE); - this.cancelableCallback = cancelableCallback; - addListener(new MapboxAnimatorListener()); - } - - private final class MapboxAnimatorListener extends AnimatorListenerAdapter { - @Override - public void onAnimationCancel(Animator animation) { - if (cancelableCallback != null) { - cancelableCallback.onCancel(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - if (cancelableCallback != null) { - cancelableCallback.onFinish(); - } - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java deleted file mode 100644 index d7d518bc8..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxFloatAnimator.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.FloatEvaluator; -import android.animation.TypeEvaluator; -import android.support.annotation.NonNull; - -class MapboxFloatAnimator extends MapboxAnimator { - MapboxFloatAnimator(Float previous, Float target, AnimationsValueChangeListener updateListener, int maxAnimationFps) { - super(previous, target, updateListener, maxAnimationFps); - } - - @NonNull - @Override - TypeEvaluator provideEvaluator() { - return new FloatEvaluator(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java deleted file mode 100644 index 507069114..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/MapboxLatLngAnimator.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.animation.TypeEvaluator; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -class MapboxLatLngAnimator extends MapboxAnimator { - - MapboxLatLngAnimator(LatLng previous, LatLng target, AnimationsValueChangeListener updateListener, - int maxAnimationFps) { - super(previous, target, updateListener, maxAnimationFps); - } - - @NonNull - @Override - TypeEvaluator provideEvaluator() { - return new LatLngEvaluator(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java deleted file mode 100644 index 6594e543f..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraMoveInvalidateListener.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -interface OnCameraMoveInvalidateListener { - - void onInvalidateCameraMove(); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java deleted file mode 100644 index 1ea80d26a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnCameraTrackingChangedListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import com.mapbox.mapboxsdk.location.modes.CameraMode; - -/** - * Listener that gets invoked when camera tracking state changes. - */ -public interface OnCameraTrackingChangedListener { - /** - * Invoked whenever camera tracking is broken. - * This callback gets invoked just after {@link #onCameraTrackingChanged(int)}, if needed. - */ - void onCameraTrackingDismissed(); - - /** - * Invoked on every {@link CameraMode} change. - * - * @param currentMode current active {@link CameraMode}. - */ - void onCameraTrackingChanged(@CameraMode.Mode int currentMode); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java deleted file mode 100644 index 824432efc..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationCameraTransitionListener.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import com.mapbox.mapboxsdk.location.modes.CameraMode; - -/** - * Callback for {@link CameraMode } transition state. - */ -public interface OnLocationCameraTransitionListener { - /** - * Invoked when the camera mode transition animation has been finished. - * - * @param cameraMode camera mode change that initiated the transition - */ - void onLocationCameraTransitionFinished(@CameraMode.Mode int cameraMode); - - /** - * Invoked when the camera mode transition animation has been canceled. - *

- * The camera mode is set regardless of the cancellation of the transition animation. - * - * @param cameraMode camera mode change that initiated the transition - */ - void onLocationCameraTransitionCanceled(@CameraMode.Mode int cameraMode); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationClickListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationClickListener.java deleted file mode 100644 index fdd2aeb81..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationClickListener.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -/** - * The Location Component exposes an API for listening to when the user clicks on the location - * layer icon visible on the map. When this event occurs, the {@link #onLocationComponentClick()} method - * gets invoked. - */ -public interface OnLocationClickListener { - - /** - * Called whenever user clicks on the location layer drawn on the map. - */ - void onLocationComponentClick(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationLongClickListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationLongClickListener.java deleted file mode 100644 index 0c34614f3..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationLongClickListener.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -/** - * The Location Component exposes an API for listening to when the user long clicks on the location - * layer icon visible on the map. when this event occurs, the {@link #onLocationComponentLongClick()} method - * gets invoked. - */ -public interface OnLocationLongClickListener { - - /** - * Called whenever user long clicks on the location layer drawn on the map. - */ - void onLocationComponentLongClick(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java deleted file mode 100644 index 6bbe7f34b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnLocationStaleListener.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -/** - * Listener that can be added as a callback when the last location update - * is considered stale. - *

- * The time from the last location update that determines if a location update - * is stale or not is provided by {@link LocationComponentOptions#staleStateTimeout()}. - */ -public interface OnLocationStaleListener { - - /** - * Called when the stale state changes. - * @param isStale true if location is stale, false otherwise - */ - void onStaleStateChange(boolean isStale); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnRenderModeChangedListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnRenderModeChangedListener.java deleted file mode 100644 index 12ac220c5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/OnRenderModeChangedListener.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import com.mapbox.mapboxsdk.location.modes.RenderMode; - -/** - * Listener that gets invoked when layer render mode changes. - */ -public interface OnRenderModeChangedListener { - - /** - * Invoked on every {@link RenderMode} change. - * - * @param currentMode current active {@link RenderMode}. - */ - void onRenderModeChanged(@RenderMode.Mode int currentMode); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java deleted file mode 100644 index 74c2e3a73..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/StaleStateManager.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.os.Handler; -import android.os.Message; -import android.support.annotation.NonNull; - -import java.lang.ref.WeakReference; - -/** - * Class controls the location stale state when the {@link android.location.Location} hasn't - * been updated in 'x' amount of time. {@link LocationComponentOptions#staleStateTimeout()} can be used to - * control the amount of time before the location's considered stale. - * {@link LocationComponentOptions#enableStaleState()} is available for disabling this behaviour. - */ -class StaleStateManager { - - private boolean isEnabled; - private final OnLocationStaleListener innerOnLocationStaleListeners; - @NonNull - private final StaleMessageHandler handler; - private boolean isStale = true; - private long delayTime; - - private final int staleStateMessage = 1; - - StaleStateManager(OnLocationStaleListener innerListener, LocationComponentOptions options) { - innerOnLocationStaleListeners = innerListener; - handler = new StaleMessageHandler(this); - isEnabled = options.enableStaleState(); - delayTime = options.staleStateTimeout(); - } - - void setEnabled(boolean enabled) { - if (enabled) { - setState(isStale); - } else if (isEnabled) { - onStop(); - innerOnLocationStaleListeners.onStaleStateChange(false); - } - isEnabled = enabled; - } - - boolean isStale() { - return isStale; - } - - void updateLatestLocationTime() { - setState(false); - postTheCallback(); - } - - void setDelayTime(long delayTime) { - this.delayTime = delayTime; - if (handler.hasMessages(staleStateMessage)) { - postTheCallback(); - } - } - - void onStart() { - if (!isStale) { - postTheCallback(); - } - } - - void onStop() { - handler.removeCallbacksAndMessages(null); - } - - private void postTheCallback() { - handler.removeCallbacksAndMessages(null); - handler.sendEmptyMessageDelayed(staleStateMessage, delayTime); - } - - private void setState(boolean stale) { - if (stale != isStale) { - isStale = stale; - if (isEnabled) { - innerOnLocationStaleListeners.onStaleStateChange(stale); - } - } - } - - private static class StaleMessageHandler extends Handler { - - private final WeakReference managerWeakReference; - - private StaleMessageHandler(StaleStateManager staleStateManager) { - this.managerWeakReference = new WeakReference<>(staleStateManager); - } - - @Override - public void handleMessage(Message msg) { - StaleStateManager manager = managerWeakReference.get(); - if (manager != null) { - manager.setState(true); - } - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java deleted file mode 100644 index 178183ca0..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/Utils.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.LayerDrawable; -import android.location.Location; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.INSTANT_LOCATION_TRANSITION_THRESHOLD; - -public final class Utils { - - private static final String TAG = "Mbgl-com.mapbox.mapboxsdk.location.Utils"; - - private Utils() { - // Class should not be initialized - } - - /** - * Util for finding the shortest path from the current rotated degree to the new degree. - * - * @param heading the new position of the rotation - * @param previousHeading the current position of the rotation - * @return the shortest degree of rotation possible - */ - public static float shortestRotation(float heading, float previousHeading) { - double diff = previousHeading - heading; - if (diff > 180.0f) { - heading += 360.0f; - } else if (diff < -180.0f) { - heading -= 360.f; - } - return heading; - } - - /** - * Normalizes an angle to be in the [0, 360] range. - * - * @param angle the provided angle - * @return the normalized angle - */ - public static float normalize(float angle) { - return (angle % 360 + 360) % 360; - } - - static Bitmap generateShadow(Drawable drawable, float elevation) { - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(bitmap); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - try { - drawable.draw(canvas); - } catch (IllegalArgumentException ex) { - if (ex.getMessage().equals("radius must be > 0") && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - Logger.w(TAG, - "Location's shadow gradient drawable has a radius <= 0px, resetting to 1px in order to avoid crashing"); - ensureShadowGradientRadius(drawable); - return generateShadow(drawable, elevation); - } else { - throw ex; - } - } - bitmap = Bitmap.createScaledBitmap(bitmap, - toEven(width + elevation), toEven(height + elevation), false); - return bitmap; - } - - /** - * We need to ensure that the radius of any {@link GradientDrawable} is greater than 0 for API levels < 21. - * - * @see mapbox-gl-native-#15026 - */ - private static void ensureShadowGradientRadius(Drawable drawable) { - if (drawable instanceof GradientDrawable) { - ((GradientDrawable) drawable).setGradientRadius(1); - } else if (drawable instanceof LayerDrawable) { - LayerDrawable layerDrawable = (LayerDrawable) drawable; - for (int i = 0; i < layerDrawable.getNumberOfLayers(); i++) { - Drawable layers = layerDrawable.getDrawable(i); - if (layers instanceof GradientDrawable) { - ((GradientDrawable) layers).setGradientRadius(1); - } - } - } - } - - static float calculateZoomLevelRadius(@NonNull MapboxMap mapboxMap, @Nullable Location location) { - if (location == null) { - return 0; - } - double metersPerPixel = mapboxMap.getProjection().getMetersPerPixelAtLatitude(location.getLatitude()); - return (float) (location.getAccuracy() * (1 / metersPerPixel)); - } - - static boolean immediateAnimation(@NonNull Projection projection, @NonNull LatLng current, @NonNull LatLng target) { - double metersPerPixel = projection.getMetersPerPixelAtLatitude((current.getLatitude() + target.getLatitude()) / 2); - double distance = current.distanceTo(target); - return distance / metersPerPixel > INSTANT_LOCATION_TRANSITION_THRESHOLD; - } - - /** - * Casts the value to an even integer. - */ - private static int toEven(float value) { - int i = (int) (value + .5f); - if (i % 2 == 1) { - return i - 1; - } - return i; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java deleted file mode 100644 index 51806a33c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/CameraMode.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.mapbox.mapboxsdk.location.modes; - -import android.location.Location; -import android.support.annotation.IntDef; - -import com.mapbox.mapboxsdk.location.LocationComponent; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains the variety of camera modes which determine how the camera will track - * the user location. - */ -public final class CameraMode { - - private CameraMode() { - // Class should not be initialized - } - - /** - * Determine the camera tracking behavior in the {@link LocationComponent}. - */ - @IntDef( {NONE, NONE_COMPASS, NONE_GPS, TRACKING, TRACKING_COMPASS, TRACKING_GPS, TRACKING_GPS_NORTH}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode { - } - - /** - * No camera tracking. - */ - public static final int NONE = 0x00000008; - - /** - * Camera does not track location, but does track compass bearing. - */ - public static final int NONE_COMPASS = 0x00000010; - - /** - * Camera does not track location, but does track GPS {@link Location} bearing. - */ - public static final int NONE_GPS = 0x00000016; - - /** - * Camera tracks the user location. - */ - public static final int TRACKING = 0x00000018; - - /** - * Camera tracks the user location, with bearing - * provided by a compass. - */ - public static final int TRACKING_COMPASS = 0x00000020; - - /** - * Camera tracks the user location, with bearing - * provided by a normalized {@link Location#getBearing()}. - */ - public static final int TRACKING_GPS = 0x00000022; - - /** - * Camera tracks the user location, with bearing - * always set to north (0). - */ - public static final int TRACKING_GPS_NORTH = 0x00000024; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java deleted file mode 100644 index 3d20313df..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/RenderMode.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.mapbox.mapboxsdk.location.modes; - -import android.support.annotation.IntDef; - -import com.mapbox.mapboxsdk.location.CompassEngine; -import com.mapbox.mapboxsdk.location.LocationComponent; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Contains the variety of ways the user location can be rendered on the map. - */ -public final class RenderMode { - - private RenderMode() { - // Class should not be initialized - } - - /** - * One of these constants should be used with {@link LocationComponent#setRenderMode(int)}. - * Mode can be switched at anytime by calling the {@code setLocationLayerMode} method passing - * in the new mode you'd like the location layer to be in. - */ - @IntDef( {COMPASS, GPS, NORMAL}) - @Retention(RetentionPolicy.SOURCE) - public @interface Mode { - } - - /** - * Basic tracking is enabled, bearing ignored. - */ - public static final int NORMAL = 0x00000012; - - /** - * Tracking the user location with bearing considered - * from a {@link CompassEngine}. - */ - public static final int COMPASS = 0x00000004; - - /** - * Tracking the user location with bearing considered from {@link android.location.Location}. - */ - public static final int GPS = 0x00000008; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/package-info.java deleted file mode 100644 index 70dd8cf27..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/modes/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Location layer component camera and render modes. - */ -package com.mapbox.mapboxsdk.location.modes; \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java deleted file mode 100644 index d37eddffc..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/location/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Location layer component. - */ -package com.mapbox.mapboxsdk.location; \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/Logger.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/Logger.java deleted file mode 100644 index b6c4bc872..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/Logger.java +++ /dev/null @@ -1,321 +0,0 @@ -package com.mapbox.mapboxsdk.log; - -import android.support.annotation.IntDef; -import android.support.annotation.Keep; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - - -/** - * Logger for the Mapbox Maps SDK for Android - *

- * Default implementation relies on {@link Log}. - * Alternative implementations can be set with {@link #setLoggerDefinition(LoggerDefinition)}. - *

- */ -@Keep -public final class Logger { - - private static final LoggerDefinition DEFAULT = new LoggerDefinition() { - - @Override - public void v(String tag, String msg) { - Log.v(tag, msg); - } - - @Override - public void v(String tag, String msg, Throwable tr) { - Log.v(tag, msg, tr); - } - - @Override - public void d(String tag, String msg) { - Log.d(tag, msg); - } - - @Override - public void d(String tag, String msg, Throwable tr) { - Log.d(tag, msg, tr); - } - - @Override - public void i(String tag, String msg) { - Log.i(tag, msg); - } - - @Override - public void i(String tag, String msg, Throwable tr) { - Log.i(tag, msg, tr); - } - - @Override - public void w(String tag, String msg) { - Log.w(tag, msg); - } - - @Override - public void w(String tag, String msg, Throwable tr) { - Log.w(tag, msg, tr); - } - - @Override - public void e(String tag, String msg) { - Log.e(tag, msg); - } - - @Override - public void e(String tag, String msg, Throwable tr) { - Log.e(tag, msg, tr); - } - }; - - private static volatile LoggerDefinition logger = DEFAULT; - - @LogLevel - private static int logLevel; - - /** - * Set the verbosity of the Logger. - *

- * This configuration can be used to have more granular control over which logs are emitted by the - * Mapbox Maps SDK for Android. - *

- * - * @param logLevel the verbosity level - */ - public static void setVerbosity(@LogLevel int logLevel) { - Logger.logLevel = logLevel; - } - - /** - * Replace the current used logger definition. - * - * @param loggerDefinition the definition of the logger - */ - public static void setLoggerDefinition(LoggerDefinition loggerDefinition) { - logger = loggerDefinition; - } - - /** - * Send a verbose log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - public static void v(String tag, String msg) { - if (logLevel <= VERBOSE) { - logger.v(tag, msg); - } - } - - /** - * Send a verbose log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - public static void v(String tag, String msg, Throwable tr) { - if (logLevel <= VERBOSE) { - logger.v(tag, msg, tr); - } - } - - /** - * Send a debug log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - public static void d(String tag, String msg) { - if (logLevel <= DEBUG) { - logger.d(tag, msg); - } - } - - /** - * Send a debug log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - public static void d(String tag, String msg, Throwable tr) { - if (logLevel <= DEBUG) { - logger.d(tag, msg, tr); - } - } - - /** - * Send an info log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - public static void i(String tag, String msg) { - if (logLevel <= INFO) { - logger.i(tag, msg); - } - } - - /** - * Send an info log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - public static void i(String tag, String msg, Throwable tr) { - if (logLevel <= INFO) { - logger.i(tag, msg, tr); - } - } - - /** - * Send a warning log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - public static void w(String tag, String msg) { - if (logLevel <= WARN) { - logger.w(tag, msg); - } - } - - /** - * Send a warning log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - public static void w(String tag, String msg, Throwable tr) { - if (logLevel <= WARN) { - logger.w(tag, msg, tr); - } - } - - /** - * Send an error log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - public static void e(String tag, String msg) { - if (logLevel <= ERROR) { - logger.e(tag, msg); - } - } - - /** - * Send an error log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - public static void e(String tag, String msg, Throwable tr) { - if (logLevel <= ERROR) { - logger.e(tag, msg, tr); - } - } - - /** - * Send a log message based on severity. - * - * @param severity the log severity - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param message The message you would like logged. - */ - public static void log(int severity, String tag, String message) { - switch (severity) { - case Log.VERBOSE: - Logger.v(tag, message); - break; - case Log.DEBUG: - Logger.d(tag, message); - break; - case Log.INFO: - Logger.i(tag, message); - break; - case Log.WARN: - Logger.w(tag, message); - break; - case Log.ERROR: - Logger.e(tag, message); - break; - default: - throw new UnsupportedOperationException(); - } - } - - /** - * Priority constant for the println method; use Logger.v - *

- * This log level will print all logs. - *

- */ - public static final int VERBOSE = Log.VERBOSE; - - /** - * Priority constant for the println method; use Logger.d. - *

- * This log level will print all logs except verbose. - *

- */ - public static final int DEBUG = Log.DEBUG; - - /** - * Priority constant for the println method; use Logger.i. - *

- * This log level will print all logs except verbose and debug. - *

- */ - public static final int INFO = Log.INFO; - - /** - * Priority constant for the println method; use Logger.w. - *

- * This log level will print only warn and error logs. - *

- */ - public static final int WARN = Log.WARN; - - /** - * Priority constant for the println method; use Logger.e. - *

- * This log level will print only error logs. - *

- */ - public static final int ERROR = Log.ERROR; - - /** - * Priority constant for the println method. - *

- * This log level won't print any logs. - *

- */ - public static final int NONE = 99; - - /** - * Log level indicates which logs are allowed to be emitted by the Mapbox Maps SDK for Android. - */ - @IntDef( {VERBOSE, DEBUG, INFO, WARN, ERROR, NONE}) - @Retention(RetentionPolicy.SOURCE) - public @interface LogLevel { - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/LoggerDefinition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/LoggerDefinition.java deleted file mode 100644 index 60b6eb000..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/LoggerDefinition.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.mapbox.mapboxsdk.log; - -/** - * Definition of a logger for the Mapbox Maps SDK for Android. - */ -public interface LoggerDefinition { - - /** - * Send a verbose log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - void v(String tag, String msg); - - /** - * Send a verbose log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - void v(String tag, String msg, Throwable tr); - - /** - * Send a debug log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - void d(String tag, String msg); - - /** - * Send a debug log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - void d(String tag, String msg, Throwable tr); - - /** - * Send an info log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - void i(String tag, String msg); - - /** - * Send an info log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - void i(String tag, String msg, Throwable tr); - - /** - * Send a warning log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - void w(String tag, String msg); - - /** - * Send a warning log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - void w(String tag, String msg, Throwable tr); - - /** - * Send an error log message. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - */ - void e(String tag, String msg); - - /** - * Send an error log message and log the exception. - * - * @param tag Used to identify the source of a log message. It usually identifies - * the class or activity where the log call occurs. - * @param msg The message you would like logged. - * @param tr An exception to log - */ - void e(String tag, String msg, Throwable tr); -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/package-info.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/package-info.java deleted file mode 100644 index 3476ff7cd..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/log/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * Contains the Mapbox Maps Android Log API classes. - */ -package com.mapbox.mapboxsdk.log; \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java deleted file mode 100644 index e5bf51279..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationContainer.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - - -import android.support.annotation.NonNull; -import android.support.v4.util.LongSparseArray; - -import com.mapbox.mapboxsdk.annotations.Annotation; - -import java.util.ArrayList; -import java.util.List; - -/** - * Encapsulates {@link Annotation}'s functionality.. - */ -class AnnotationContainer implements Annotations { - - private final NativeMap nativeMap; - private final LongSparseArray annotations; - - AnnotationContainer(NativeMap nativeMap, LongSparseArray annotations) { - this.nativeMap = nativeMap; - this.annotations = annotations; - } - - @Override - public Annotation obtainBy(long id) { - return annotations.get(id); - } - - @NonNull - @Override - public List obtainAll() { - List annotations = new ArrayList<>(); - for (int i = 0; i < this.annotations.size(); i++) { - annotations.add(this.annotations.get(this.annotations.keyAt(i))); - } - return annotations; - } - - @Override - public void removeBy(long id) { - if (nativeMap != null) { - nativeMap.removeAnnotation(id); - } - annotations.remove(id); - } - - @Override - public void removeBy(@NonNull Annotation annotation) { - long id = annotation.getId(); - removeBy(id); - } - - @Override - public void removeBy(@NonNull List annotationList) { - int count = annotationList.size(); - long[] ids = new long[count]; - for (int i = 0; i < count; i++) { - ids[i] = annotationList.get(i).getId(); - } - - removeNativeAnnotations(ids); - - for (long id : ids) { - annotations.remove(id); - } - } - - @Override - public void removeAll() { - int count = annotations.size(); - long[] ids = new long[count]; - for (int i = 0; i < count; i++) { - ids[i] = annotations.keyAt(i); - } - - removeNativeAnnotations(ids); - - annotations.clear(); - } - - private void removeNativeAnnotations(long[] ids) { - if (nativeMap != null) { - nativeMap.removeAnnotations(ids); - } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java deleted file mode 100644 index 8875e7164..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AnnotationManager.java +++ /dev/null @@ -1,519 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.graphics.Rect; -import android.graphics.RectF; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.util.LongSparseArray; -import android.view.View; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.PolygonOptions; -import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.annotations.PolylineOptions; -import com.mapbox.mapboxsdk.log.Logger; - -import java.util.ArrayList; -import java.util.List; - -/** - * Responsible for managing and tracking state of Annotations linked to Map. All events related to - * annotations that occur on {@link MapboxMap} are forwarded to this class. - *

- * Responsible for referencing {@link InfoWindowManager}. - *

- *

- * Exposes convenience methods to add/remove/update all subtypes of annotations found in - * com.mapbox.mapboxsdk.annotations. - *

- */ -class AnnotationManager { - - private static final String TAG = "Mbgl-AnnotationManager"; - - private static final long NO_ANNOTATION_ID = -1; - - @NonNull - private final MapView mapView; - private final IconManager iconManager; - private final InfoWindowManager infoWindowManager = new InfoWindowManager(); - private final LongSparseArray annotationsArray; - private final List selectedMarkers = new ArrayList<>(); - - private MapboxMap mapboxMap; - @Nullable - private MapboxMap.OnMarkerClickListener onMarkerClickListener; - @Nullable - private MapboxMap.OnPolygonClickListener onPolygonClickListener; - @Nullable - private MapboxMap.OnPolylineClickListener onPolylineClickListener; - - private Annotations annotations; - private ShapeAnnotations shapeAnnotations; - private Markers markers; - private Polygons polygons; - private Polylines polylines; - - AnnotationManager(@NonNull MapView mapView, LongSparseArray annotationsArray, - IconManager iconManager, Annotations annotations, Markers markers, Polygons polygons, - Polylines polylines, ShapeAnnotations shapeAnnotations) { - this.mapView = mapView; - this.annotationsArray = annotationsArray; - this.iconManager = iconManager; - this.annotations = annotations; - this.markers = markers; - this.polygons = polygons; - this.polylines = polylines; - this.shapeAnnotations = shapeAnnotations; - } - - // TODO refactor MapboxMap out for Projection and Transform - // Requires removing MapboxMap from Annotations by using Peer model from #6912 - @NonNull - AnnotationManager bind(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - return this; - } - - void update() { - infoWindowManager.update(); - } - - // - // Annotations - // - - Annotation getAnnotation(long id) { - return annotations.obtainBy(id); - } - - List getAnnotations() { - return annotations.obtainAll(); - } - - void removeAnnotation(long id) { - annotations.removeBy(id); - } - - void removeAnnotation(@NonNull Annotation annotation) { - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.hideInfoWindow(); - if (selectedMarkers.contains(marker)) { - selectedMarkers.remove(marker); - } - // do icon cleanup - iconManager.iconCleanup(marker.getIcon()); - } - annotations.removeBy(annotation); - } - - void removeAnnotations(@NonNull List annotationList) { - for (Annotation annotation : annotationList) { - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.hideInfoWindow(); - if (selectedMarkers.contains(marker)) { - selectedMarkers.remove(marker); - } - iconManager.iconCleanup(marker.getIcon()); - } - } - annotations.removeBy(annotationList); - } - - void removeAnnotations() { - Annotation annotation; - int count = annotationsArray.size(); - long[] ids = new long[count]; - selectedMarkers.clear(); - for (int i = 0; i < count; i++) { - ids[i] = annotationsArray.keyAt(i); - annotation = annotationsArray.get(ids[i]); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.hideInfoWindow(); - iconManager.iconCleanup(marker.getIcon()); - } - } - annotations.removeAll(); - } - - // - // Markers - // - - Marker addMarker(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) { - return markers.addBy(markerOptions, mapboxMap); - } - - List addMarkers(@NonNull List markerOptionsList, @NonNull MapboxMap mapboxMap) { - return markers.addBy(markerOptionsList, mapboxMap); - } - - void updateMarker(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { - if (!isAddedToMap(updatedMarker)) { - logNonAdded(updatedMarker); - return; - } - markers.update(updatedMarker, mapboxMap); - } - - List getMarkers() { - return markers.obtainAll(); - } - - @NonNull - List getMarkersInRect(@NonNull RectF rectangle) { - return markers.obtainAllIn(rectangle); - } - - void reloadMarkers() { - markers.reload(); - } - - // - // Polygons - // - - Polygon addPolygon(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { - return polygons.addBy(polygonOptions, mapboxMap); - } - - List addPolygons(@NonNull List polygonOptionsList, @NonNull MapboxMap mapboxMap) { - return polygons.addBy(polygonOptionsList, mapboxMap); - } - - void updatePolygon(@NonNull Polygon polygon) { - if (!isAddedToMap(polygon)) { - logNonAdded(polygon); - return; - } - polygons.update(polygon); - } - - List getPolygons() { - return polygons.obtainAll(); - } - - // - // Polylines - // - - Polyline addPolyline(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { - return polylines.addBy(polylineOptions, mapboxMap); - } - - List addPolylines(@NonNull List polylineOptionsList, @NonNull MapboxMap mapboxMap) { - return polylines.addBy(polylineOptionsList, mapboxMap); - } - - void updatePolyline(@NonNull Polyline polyline) { - if (!isAddedToMap(polyline)) { - logNonAdded(polyline); - return; - } - polylines.update(polyline); - } - - List getPolylines() { - return polylines.obtainAll(); - } - - // TODO Refactor from here still in progress - void setOnMarkerClickListener(@Nullable MapboxMap.OnMarkerClickListener listener) { - onMarkerClickListener = listener; - } - - void setOnPolygonClickListener(@Nullable MapboxMap.OnPolygonClickListener listener) { - onPolygonClickListener = listener; - } - - void setOnPolylineClickListener(@Nullable MapboxMap.OnPolylineClickListener listener) { - onPolylineClickListener = listener; - } - - void selectMarker(@NonNull Marker marker) { - if (selectedMarkers.contains(marker)) { - return; - } - - // Need to deselect any currently selected annotation first - if (!infoWindowManager.isAllowConcurrentMultipleOpenInfoWindows()) { - deselectMarkers(); - } - - if (infoWindowManager.isInfoWindowValidForMarker(marker) || infoWindowManager.getInfoWindowAdapter() != null) { - infoWindowManager.add(marker.showInfoWindow(mapboxMap, mapView)); - } - - // only add to selected markers if user didn't handle the click event themselves #3176 - selectedMarkers.add(marker); - } - - void deselectMarkers() { - if (selectedMarkers.isEmpty()) { - return; - } - - for (Marker marker : selectedMarkers) { - if (marker != null) { - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - } - } - } - - // Removes all selected markers from the list - selectedMarkers.clear(); - } - - void deselectMarker(@NonNull Marker marker) { - if (!selectedMarkers.contains(marker)) { - return; - } - - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - } - selectedMarkers.remove(marker); - } - - @NonNull - List getSelectedMarkers() { - return selectedMarkers; - } - - @NonNull - InfoWindowManager getInfoWindowManager() { - return infoWindowManager; - } - - void adjustTopOffsetPixels(@NonNull MapboxMap mapboxMap) { - int count = annotationsArray.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotationsArray.get(i); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - marker.setTopOffsetPixels( - iconManager.getTopOffsetPixelsForIcon(marker.getIcon())); - } - } - - for (Marker marker : selectedMarkers) { - if (marker.isInfoWindowShown()) { - marker.hideInfoWindow(); - marker.showInfoWindow(mapboxMap, mapView); - } - } - } - - private boolean isAddedToMap(@Nullable Annotation annotation) { - return annotation != null && annotation.getId() != -1 && annotationsArray.indexOfKey(annotation.getId()) > -1; - } - - private void logNonAdded(@NonNull Annotation annotation) { - Logger.w(TAG, String.format( - "Attempting to update non-added %s with value %s", annotation.getClass().getCanonicalName(), annotation) - ); - } - - // - // Click event - // - - boolean onTap(@NonNull PointF tapPoint) { - MarkerHit markerHit = getMarkerHitFromTouchArea(tapPoint); - long markerId = new MarkerHitResolver(mapboxMap).execute(markerHit); - if (markerId != NO_ANNOTATION_ID) { - if (isClickHandledForMarker(markerId)) { - return true; - } - } - - ShapeAnnotationHit shapeAnnotationHit = getShapeAnnotationHitFromTap(tapPoint); - Annotation annotation = new ShapeAnnotationHitResolver(shapeAnnotations).execute(shapeAnnotationHit); - return annotation != null && handleClickForShapeAnnotation(annotation); - } - - private ShapeAnnotationHit getShapeAnnotationHitFromTap(PointF tapPoint) { - float touchTargetSide = Mapbox.getApplicationContext().getResources().getDimension(R.dimen.mapbox_eight_dp); - RectF tapRect = new RectF( - tapPoint.x - touchTargetSide, - tapPoint.y - touchTargetSide, - tapPoint.x + touchTargetSide, - tapPoint.y + touchTargetSide - ); - return new ShapeAnnotationHit(tapRect); - } - - private boolean handleClickForShapeAnnotation(Annotation annotation) { - if (annotation instanceof Polygon && onPolygonClickListener != null) { - onPolygonClickListener.onPolygonClick((Polygon) annotation); - return true; - } else if (annotation instanceof Polyline && onPolylineClickListener != null) { - onPolylineClickListener.onPolylineClick((Polyline) annotation); - return true; - } - return false; - } - - private MarkerHit getMarkerHitFromTouchArea(PointF tapPoint) { - int touchSurfaceWidth = (int) (iconManager.getHighestIconHeight() * 1.5); - int touchSurfaceHeight = (int) (iconManager.getHighestIconWidth() * 1.5); - final RectF tapRect = new RectF(tapPoint.x - touchSurfaceWidth, - tapPoint.y - touchSurfaceHeight, - tapPoint.x + touchSurfaceWidth, - tapPoint.y + touchSurfaceHeight - ); - return new MarkerHit(tapRect, getMarkersInRect(tapRect)); - } - - private boolean isClickHandledForMarker(long markerId) { - Marker marker = (Marker) getAnnotation(markerId); - boolean handledDefaultClick = onClickMarker(marker); - if (!handledDefaultClick) { - toggleMarkerSelectionState(marker); - } - return true; - } - - private boolean onClickMarker(@NonNull Marker marker) { - return onMarkerClickListener != null && onMarkerClickListener.onMarkerClick(marker); - } - - private void toggleMarkerSelectionState(@NonNull Marker marker) { - if (!selectedMarkers.contains(marker)) { - selectMarker(marker); - } else { - deselectMarker(marker); - } - } - - private static class ShapeAnnotationHitResolver { - - private ShapeAnnotations shapeAnnotations; - - ShapeAnnotationHitResolver(ShapeAnnotations shapeAnnotations) { - this.shapeAnnotations = shapeAnnotations; - } - - @Nullable - public Annotation execute(@NonNull ShapeAnnotationHit shapeHit) { - Annotation foundAnnotation = null; - List annotations = shapeAnnotations.obtainAllIn(shapeHit.tapPoint); - if (annotations.size() > 0) { - foundAnnotation = annotations.get(0); - } - return foundAnnotation; - } - } - - private static class MarkerHitResolver { - - @NonNull - private final Projection projection; - private final int minimalTouchSize; - - @Nullable - private View view; - - private Bitmap bitmap; - private int bitmapWidth; - private int bitmapHeight; - private PointF markerLocation; - - @NonNull - private Rect hitRectView = new Rect(); - @NonNull - private RectF hitRectMarker = new RectF(); - @NonNull - private RectF highestSurfaceIntersection = new RectF(); - - private long closestMarkerId = NO_ANNOTATION_ID; - - MarkerHitResolver(@NonNull MapboxMap mapboxMap) { - this.projection = mapboxMap.getProjection(); - this.minimalTouchSize = (int) (32 * Mapbox.getApplicationContext().getResources().getDisplayMetrics().density); - } - - public long execute(@NonNull MarkerHit markerHit) { - resolveForMarkers(markerHit); - return closestMarkerId; - } - - private void resolveForMarkers(MarkerHit markerHit) { - for (Marker marker : markerHit.markers) { - resolveForMarker(markerHit, marker); - } - } - - private void resolveForMarker(@NonNull MarkerHit markerHit, Marker marker) { - markerLocation = projection.toScreenLocation(marker.getPosition()); - bitmap = marker.getIcon().getBitmap(); - - bitmapHeight = bitmap.getHeight(); - if (bitmapHeight < minimalTouchSize) { - bitmapHeight = minimalTouchSize; - } - - bitmapWidth = bitmap.getWidth(); - if (bitmapWidth < minimalTouchSize) { - bitmapWidth = minimalTouchSize; - } - - hitRectMarker.set(0, 0, bitmapWidth, bitmapHeight); - hitRectMarker.offsetTo( - markerLocation.x - bitmapWidth / 2, - markerLocation.y - bitmapHeight / 2 - ); - hitTestMarker(markerHit, marker, hitRectMarker); - } - - private void hitTestMarker(MarkerHit markerHit, @NonNull Marker marker, RectF hitRectMarker) { - if (hitRectMarker.contains(markerHit.getTapPointX(), markerHit.getTapPointY())) { - hitRectMarker.intersect(markerHit.tapRect); - if (isRectangleHighestSurfaceIntersection(hitRectMarker)) { - highestSurfaceIntersection = new RectF(hitRectMarker); - closestMarkerId = marker.getId(); - } - } - } - - private boolean isRectangleHighestSurfaceIntersection(RectF rectF) { - return rectF.width() * rectF.height() > highestSurfaceIntersection.width() * highestSurfaceIntersection.height(); - } - } - - private static class ShapeAnnotationHit { - private final RectF tapPoint; - - ShapeAnnotationHit(RectF tapPoint) { - this.tapPoint = tapPoint; - } - } - - private static class MarkerHit { - private final RectF tapRect; - private final List markers; - - MarkerHit(RectF tapRect, List markers) { - this.tapRect = tapRect; - this.markers = markers; - } - - float getTapPointX() { - return tapRect.centerX(); - } - - float getTapPointY() { - return tapRect.centerY(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java deleted file mode 100644 index ae41cbb0c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Annotations.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - - -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.annotations.Annotation; - -import java.util.List; - -/** - * Interface that defines convenient methods for working with a {@link Annotation}'s collection. - */ -interface Annotations { - Annotation obtainBy(long id); - - List obtainAll(); - - void removeBy(long id); - - void removeBy(@NonNull Annotation annotation); - - void removeBy(@NonNull List annotationList); - - void removeAll(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java deleted file mode 100644 index 5384a29fc..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/AttributionDialogManager.java +++ /dev/null @@ -1,244 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.app.Activity; -import android.app.AlertDialog; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.View; -import android.widget.ArrayAdapter; -import android.widget.Toast; -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.attribution.Attribution; -import com.mapbox.mapboxsdk.attribution.AttributionParser; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.style.sources.Source; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Locale; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Responsible for managing attribution interactions on the map. - *

- * When the user clicks the attribution icon, {@link AttributionDialogManager#onClick(View)} will be invoked. - * An attribution dialog will be shown to the user with contents based on the attributions found in the map style. - * Additionally an telemetry option item is shown to configure telemetry settings. - *

- */ -public class AttributionDialogManager implements View.OnClickListener, DialogInterface.OnClickListener { - private static final String MAP_FEEDBACK_URL = "https://apps.mapbox.com/feedback"; - private static final String MAP_FEEDBACK_URL_OLD = "https://www.mapbox.com/map-feedback"; - private static final String MAP_FEEDBACK_URL_LOCATION_FRAGMENT_FORMAT = "/%f/%f/%f/%f/%d"; - private static final String MAP_FEEDBACK_STYLE_URI_REGEX = "^(.*://[^:^/]*)/(.*)/(.*)"; - - @NonNull - private final Context context; - @NonNull - private final MapboxMap mapboxMap; - private Set attributionSet; - private AlertDialog dialog; - - public AttributionDialogManager(@NonNull Context context, @NonNull MapboxMap mapboxMap) { - this.context = context; - this.mapboxMap = mapboxMap; - } - - // Called when someone presses the attribution icon on the map - @Override - public void onClick(@NonNull View view) { - attributionSet = new AttributionBuilder(mapboxMap, view.getContext()).build(); - - boolean isActivityFinishing = false; - if (context instanceof Activity) { - isActivityFinishing = ((Activity) context).isFinishing(); - } - - // check is hosting activity isn't finishing - // https://github.com/mapbox/mapbox-gl-native/issues/11238 - if (!isActivityFinishing) { - showAttributionDialog(getAttributionTitles()); - } - } - - protected void showAttributionDialog(@NonNull String[] attributionTitles) { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.mapbox_attributionsDialogTitle); - builder.setAdapter(new ArrayAdapter<>(context, R.layout.mapbox_attribution_list_item, attributionTitles), this); - dialog = builder.show(); - } - - private String[] getAttributionTitles() { - List titles = new ArrayList<>(); - for (Attribution attribution : attributionSet) { - titles.add(attribution.getTitle()); - } - return titles.toArray(new String[titles.size()]); - } - - // Called when someone selects an attribution or telemetry settings from the dialog - @Override - public void onClick(DialogInterface dialog, int which) { - if (isLatestEntry(which)) { - showTelemetryDialog(); - } else { - showMapAttributionWebPage(which); - } - } - - public void onStop() { - if (dialog != null && dialog.isShowing()) { - dialog.dismiss(); - } - } - - private boolean isLatestEntry(int attributionKeyIndex) { - return attributionKeyIndex == getAttributionTitles().length - 1; - } - - private void showTelemetryDialog() { - AlertDialog.Builder builder = new AlertDialog.Builder(context); - builder.setTitle(R.string.mapbox_attributionTelemetryTitle); - builder.setMessage(R.string.mapbox_attributionTelemetryMessage); - builder.setPositiveButton(R.string.mapbox_attributionTelemetryPositive, new DialogInterface.OnClickListener() { - @Override - public void onClick(@NonNull DialogInterface dialog, int which) { - TelemetryDefinition telemetry = Mapbox.getTelemetry(); - if (telemetry != null) { - telemetry.setUserTelemetryRequestState(true); - } - dialog.cancel(); - } - }); - builder.setNeutralButton(R.string.mapbox_attributionTelemetryNeutral, new DialogInterface.OnClickListener() { - @Override - public void onClick(@NonNull DialogInterface dialog, int which) { - showWebPage(context.getResources().getString(R.string.mapbox_telemetryLink)); - dialog.cancel(); - } - }); - builder.setNegativeButton(R.string.mapbox_attributionTelemetryNegative, new DialogInterface.OnClickListener() { - @Override - public void onClick(@NonNull DialogInterface dialog, int which) { - TelemetryDefinition telemetry = Mapbox.getTelemetry(); - if (telemetry != null) { - telemetry.setUserTelemetryRequestState(false); - } - dialog.cancel(); - } - }); - builder.show(); - } - - private void showMapAttributionWebPage(int which) { - Attribution[] attributions = attributionSet.toArray(new Attribution[attributionSet.size()]); - String url = attributions[which].getUrl(); - if (url.contains(MAP_FEEDBACK_URL_OLD) || url.contains(MAP_FEEDBACK_URL)) { - url = buildMapFeedbackMapUrl(Mapbox.getAccessToken()); - } - showWebPage(url); - } - - @NonNull - String buildMapFeedbackMapUrl(@Nullable String accessToken) { - // TODO Add Android Maps SDK version to the query parameter, currently the version API is not available. - // TODO Keep track of this issue at [#15632](https://github.com/mapbox/mapbox-gl-native/issues/15632) - - Uri.Builder builder = Uri.parse(MAP_FEEDBACK_URL).buildUpon(); - - CameraPosition cameraPosition = mapboxMap.getCameraPosition(); - if (cameraPosition != null) { - builder.encodedFragment(String.format(Locale.getDefault(), MAP_FEEDBACK_URL_LOCATION_FRAGMENT_FORMAT, - cameraPosition.target.getLongitude(), cameraPosition.target.getLatitude(), - cameraPosition.zoom, cameraPosition.bearing, (int) cameraPosition.tilt)); - } - - String packageName = context.getApplicationContext().getPackageName(); - if (packageName != null) { - builder.appendQueryParameter("referrer", packageName); - } - - if (accessToken != null) { - builder.appendQueryParameter("access_token", accessToken); - } - - Style style = mapboxMap.getStyle(); - if (style != null) { - String styleUri = style.getUri(); - Pattern pattern = Pattern.compile(MAP_FEEDBACK_STYLE_URI_REGEX); - Matcher matcher = pattern.matcher(styleUri); - - if (matcher.find()) { - String styleOwner = matcher.group(2); - String styleId = matcher.group(3); - - builder.appendQueryParameter("owner", styleOwner) - .appendQueryParameter("id", styleId); - } - } - - return builder.build().toString(); - } - - private void showWebPage(@NonNull String url) { - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - context.startActivity(intent); - } catch (ActivityNotFoundException exception) { - // explicitly handling if the device hasn't have a web browser installed. #8899 - Toast.makeText(context, R.string.mapbox_attributionErrorNoBrowser, Toast.LENGTH_LONG).show(); - MapStrictMode.strictModeViolation(exception); - } - } - - private static class AttributionBuilder { - - private final MapboxMap mapboxMap; - @NonNull - private final WeakReference context; - - AttributionBuilder(MapboxMap mapboxMap, Context context) { - this.mapboxMap = mapboxMap; - this.context = new WeakReference<>(context); - } - - private Set build() { - Context context = this.context.get(); - if (context == null) { - return Collections.emptySet(); - } - - List attributions = new ArrayList<>(); - String attribution; - - Style style = mapboxMap.getStyle(); - if (style != null) { - for (Source source : style.getSources()) { - attribution = source.getAttribution(); - if (!attribution.isEmpty()) { - attributions.add(attribution); - } - } - } - - return new AttributionParser.Options(context) - .withCopyrightSign(true) - .withImproveMap(true) - .withTelemetryAttribution(true) - .withAttributionData(attributions.toArray(new String[attributions.size()])) - .build().getAttributions(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java deleted file mode 100644 index a1fd4e7e3..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraChangeDispatcher.java +++ /dev/null @@ -1,204 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.os.Handler; -import android.os.Message; -import android.support.annotation.IntDef; -import android.support.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.ref.WeakReference; -import java.util.concurrent.CopyOnWriteArrayList; - -import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraIdleListener; -import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveCanceledListener; -import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveListener; -import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener; -import static java.lang.annotation.RetentionPolicy.SOURCE; - -/** - * Class responsible for dispatching camera change events to registered listeners. - */ -class CameraChangeDispatcher implements MapboxMap.OnCameraMoveStartedListener, MapboxMap.OnCameraMoveListener, - MapboxMap.OnCameraMoveCanceledListener, OnCameraIdleListener { - - private final CameraChangeHandler handler = new CameraChangeHandler(this); - - private boolean idle = true; - private int moveStartedReason; - - private final CopyOnWriteArrayList onCameraMoveStarted = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onCameraMoveCanceled = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onCameraMove = new CopyOnWriteArrayList<>(); - private final CopyOnWriteArrayList onCameraIdle = new CopyOnWriteArrayList<>(); - - @Retention(SOURCE) - @IntDef( {MOVE_STARTED, MOVE, MOVE_CANCELED, IDLE}) - @interface CameraChange { - } - - private static final int MOVE_STARTED = 0; - private static final int MOVE = 1; - private static final int MOVE_CANCELED = 2; - private static final int IDLE = 3; - - @Override - public void onCameraMoveStarted(final int reason) { - moveStartedReason = reason; - handler.scheduleMessage(MOVE_STARTED); - } - - @Override - public void onCameraMove() { - handler.scheduleMessage(MOVE); - } - - @Override - public void onCameraMoveCanceled() { - handler.scheduleMessage(MOVE_CANCELED); - } - - @Override - public void onCameraIdle() { - handler.scheduleMessage(IDLE); - } - - void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) { - onCameraIdle.add(listener); - } - - void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) { - if (onCameraIdle.contains(listener)) { - onCameraIdle.remove(listener); - } - } - - void addOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) { - onCameraMoveCanceled.add(listener); - } - - void removeOnCameraMoveCancelListener(OnCameraMoveCanceledListener listener) { - if (onCameraMoveCanceled.contains(listener)) { - onCameraMoveCanceled.remove(listener); - } - } - - void addOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) { - onCameraMoveStarted.add(listener); - } - - void removeOnCameraMoveStartedListener(OnCameraMoveStartedListener listener) { - if (onCameraMoveStarted.contains(listener)) { - onCameraMoveStarted.remove(listener); - } - } - - void addOnCameraMoveListener(OnCameraMoveListener listener) { - onCameraMove.add(listener); - } - - void removeOnCameraMoveListener(OnCameraMoveListener listener) { - if (onCameraMove.contains(listener)) { - onCameraMove.remove(listener); - } - } - - private void executeOnCameraMoveStarted() { - if (!idle) { - return; - } - idle = false; - if (!onCameraMoveStarted.isEmpty()) { - for (OnCameraMoveStartedListener cameraMoveStartedListener : onCameraMoveStarted) { - cameraMoveStartedListener.onCameraMoveStarted(moveStartedReason); - } - } - } - - private void executeOnCameraMove() { - if (!onCameraMove.isEmpty() && !idle) { - for (OnCameraMoveListener cameraMoveListener : onCameraMove) { - cameraMoveListener.onCameraMove(); - } - } - } - - private void executeOnCameraMoveCancelled() { - if (!onCameraMoveCanceled.isEmpty() && !idle) { - for (OnCameraMoveCanceledListener cameraMoveCanceledListener : onCameraMoveCanceled) { - cameraMoveCanceledListener.onCameraMoveCanceled(); - } - } - } - - private void executeOnCameraIdle() { - if (idle) { - return; - } - idle = true; - if (!onCameraIdle.isEmpty()) { - for (OnCameraIdleListener cameraIdleListener : onCameraIdle) { - cameraIdleListener.onCameraIdle(); - } - } - } - - void onDestroy() { - handler.removeCallbacksAndMessages(null); - onCameraMoveStarted.clear(); - onCameraMoveCanceled.clear(); - onCameraMove.clear(); - onCameraIdle.clear(); - } - - private static class CameraChangeHandler extends Handler { - - private WeakReference dispatcherWeakReference; - - CameraChangeHandler(CameraChangeDispatcher dispatcher) { - super(); - this.dispatcherWeakReference = new WeakReference<>(dispatcher); - } - - @Override - public void handleMessage(@NonNull Message msg) { - CameraChangeDispatcher dispatcher = dispatcherWeakReference.get(); - if (dispatcher != null) { - switch (msg.what) { - case MOVE_STARTED: - dispatcher.executeOnCameraMoveStarted(); - break; - case MOVE: - dispatcher.executeOnCameraMove(); - break; - case MOVE_CANCELED: - dispatcher.executeOnCameraMoveCancelled(); - break; - case IDLE: - dispatcher.executeOnCameraIdle(); - break; - } - } - } - - void scheduleMessage(@CameraChange int change) { - CameraChangeDispatcher dispatcher = dispatcherWeakReference.get(); - if (dispatcher != null) { - // if there is a movement that is cancelled/stopped and restarted in the same code block - // we can safely assume that the movement will continue, no need for dispatching unnecessary callbacks sequence - if (change == MOVE_STARTED) { - boolean shouldReturn = !dispatcher.idle && (hasMessages(IDLE) || hasMessages(MOVE_CANCELED)); - removeMessages(IDLE); - removeMessages(MOVE_CANCELED); - - if (shouldReturn) { - return; - } - } - - Message message = new Message(); - message.what = change; - this.sendMessage(message); - } - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java deleted file mode 100644 index aec9a848b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/FocalPointChangeListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.PointF; - -/** - * Interface definition of a callback that is invoked when the focal point will change. - */ -public interface FocalPointChangeListener { - - void onFocalPointChanged(PointF pointF); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java deleted file mode 100644 index 3a0824ad1..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/IconManager.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.Bitmap; - -import android.support.annotation.NonNull; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.IconFactory; -import com.mapbox.mapboxsdk.annotations.Marker; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Responsible for managing icons added to the Map. - *

- * Maintains a {@link List} of {@link Icon} and is responsible for initialising default markers. - *

- *

- * Keep track of icons added and the resulting average icon size. This is used internally by our - * gestures detection to calculate the size of a touch target. - *

- */ -class IconManager { - - private final Map iconMap = new HashMap<>(); - - private NativeMap nativeMap; - private int highestIconWidth; - private int highestIconHeight; - - IconManager(NativeMap nativeMap) { - this.nativeMap = nativeMap; - } - - Icon loadIconForMarker(@NonNull Marker marker) { - Icon icon = marker.getIcon(); - if (icon == null) { - // TODO replace with anchor implementation, we are faking an anchor by adding extra pixels and diving height by 2 - icon = loadDefaultIconForMarker(marker); - } else { - updateHighestIconSize(icon); - } - addIcon(icon); - return icon; - } - - int getTopOffsetPixelsForIcon(@NonNull Icon icon) { - return (int) (nativeMap.getTopOffsetPixelsForAnnotationSymbol(icon.getId()) * nativeMap.getPixelRatio()); - } - - int getHighestIconWidth() { - return highestIconWidth; - } - - int getHighestIconHeight() { - return highestIconHeight; - } - - private Icon loadDefaultIconForMarker(Marker marker) { - Icon icon = IconFactory.getInstance(Mapbox.getApplicationContext()).defaultMarker(); - Bitmap bitmap = icon.getBitmap(); - updateHighestIconSize(bitmap.getWidth(), bitmap.getHeight() / 2); - marker.setIcon(icon); - return icon; - } - - private void addIcon(@NonNull Icon icon) { - addIcon(icon, true); - } - - private void addIcon(@NonNull Icon icon, boolean addIconToMap) { - if (!iconMap.keySet().contains(icon)) { - iconMap.put(icon, 1); - if (addIconToMap) { - loadIcon(icon); - } - } else { - iconMap.put(icon, iconMap.get(icon) + 1); - } - } - - private void updateHighestIconSize(Icon icon) { - updateHighestIconSize(icon.getBitmap()); - } - - private void updateHighestIconSize(Bitmap bitmap) { - updateHighestIconSize(bitmap.getWidth(), bitmap.getHeight()); - } - - private void updateHighestIconSize(int width, int height) { - if (width > highestIconWidth) { - highestIconWidth = width; - } - - if (height > highestIconHeight) { - highestIconHeight = height; - } - } - - private void loadIcon(Icon icon) { - Bitmap bitmap = icon.getBitmap(); - nativeMap.addAnnotationIcon(icon.getId(), - bitmap.getWidth(), - bitmap.getHeight(), - icon.getScale(), - icon.toBytes()); - } - - void reloadIcons() { - for (Icon icon : iconMap.keySet()) { - loadIcon(icon); - } - } - - void ensureIconLoaded(@NonNull Marker marker, @NonNull MapboxMap mapboxMap) { - Icon icon = marker.getIcon(); - if (icon == null) { - icon = loadDefaultIconForMarker(marker); - } - addIcon(icon); - setTopOffsetPixels(marker, mapboxMap, icon); - } - - private void setTopOffsetPixels(Marker marker, @NonNull MapboxMap mapboxMap, @NonNull Icon icon) { - // this seems to be a costly operation according to the profiler so I'm trying to save some calls - Marker previousMarker = marker.getId() != -1 ? (Marker) mapboxMap.getAnnotation(marker.getId()) : null; - if (previousMarker == null || previousMarker.getIcon() == null || previousMarker.getIcon() != marker.getIcon()) { - marker.setTopOffsetPixels(getTopOffsetPixelsForIcon(icon)); - } - } - - void iconCleanup(@NonNull Icon icon) { - Integer refCounter = iconMap.get(icon); - if (refCounter != null) { - refCounter--; - if (refCounter == 0) { - remove(icon); - } else { - updateIconRefCounter(icon, refCounter); - } - } - } - - private void remove(Icon icon) { - nativeMap.removeAnnotationIcon(icon.getId()); - iconMap.remove(icon); - } - - private void updateIconRefCounter(Icon icon, int refCounter) { - iconMap.put(icon, refCounter); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java deleted file mode 100644 index a261c2f4b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.support.annotation.Keep; - -@Keep -class Image { - private final byte[] buffer; - private final float pixelRatio; - private final String name; - private final int width; - private final int height; - private final boolean sdf; - - public Image(byte[] buffer, float pixelRatio, String name, int width, int height, boolean sdf) { - this.buffer = buffer; - this.pixelRatio = pixelRatio; - this.name = name; - this.width = width; - this.height = height; - this.sdf = sdf; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java deleted file mode 100644 index 434244a7c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/InfoWindowManager.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.support.annotation.Nullable; -import android.text.TextUtils; - -import com.mapbox.mapboxsdk.annotations.InfoWindow; -import com.mapbox.mapboxsdk.annotations.Marker; - -import java.util.ArrayList; -import java.util.List; - -/** - * Responsible for managing InfoWindows shown on the Map. - *

- * Maintains a {@link List} of opened {@link InfoWindow} and tracks configurations as - * allowConcurrentMultipleInfoWindows which allows to have multiple {@link InfoWindow} open at the - * same time. Responsible for managing listeners as - * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnInfoWindowClickListener} and - * {@link com.mapbox.mapboxsdk.maps.MapboxMap.OnInfoWindowLongClickListener}. - *

- */ -class InfoWindowManager { - - private final List infoWindows = new ArrayList<>(); - - @Nullable - private MapboxMap.InfoWindowAdapter infoWindowAdapter; - private boolean allowConcurrentMultipleInfoWindows; - - @Nullable - private MapboxMap.OnInfoWindowClickListener onInfoWindowClickListener; - @Nullable - private MapboxMap.OnInfoWindowLongClickListener onInfoWindowLongClickListener; - @Nullable - private MapboxMap.OnInfoWindowCloseListener onInfoWindowCloseListener; - - void update() { - if (!infoWindows.isEmpty()) { - for (InfoWindow infoWindow : infoWindows) { - infoWindow.update(); - } - } - } - - void setInfoWindowAdapter(@Nullable MapboxMap.InfoWindowAdapter infoWindowAdapter) { - this.infoWindowAdapter = infoWindowAdapter; - } - - @Nullable - MapboxMap.InfoWindowAdapter getInfoWindowAdapter() { - return infoWindowAdapter; - } - - void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { - allowConcurrentMultipleInfoWindows = allow; - } - - boolean isAllowConcurrentMultipleOpenInfoWindows() { - return allowConcurrentMultipleInfoWindows; - } - - boolean isInfoWindowValidForMarker(@Nullable Marker marker) { - return marker != null && (!TextUtils.isEmpty(marker.getTitle()) || !TextUtils.isEmpty(marker.getSnippet())); - } - - void setOnInfoWindowClickListener(@Nullable MapboxMap.OnInfoWindowClickListener listener) { - onInfoWindowClickListener = listener; - } - - @Nullable - MapboxMap.OnInfoWindowClickListener getOnInfoWindowClickListener() { - return onInfoWindowClickListener; - } - - void setOnInfoWindowLongClickListener(@Nullable MapboxMap.OnInfoWindowLongClickListener listener) { - onInfoWindowLongClickListener = listener; - } - - @Nullable - MapboxMap.OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { - return onInfoWindowLongClickListener; - } - - void setOnInfoWindowCloseListener(@Nullable MapboxMap.OnInfoWindowCloseListener listener) { - onInfoWindowCloseListener = listener; - } - - @Nullable - MapboxMap.OnInfoWindowCloseListener getOnInfoWindowCloseListener() { - return onInfoWindowCloseListener; - } - - public void add(InfoWindow infoWindow) { - infoWindows.add(infoWindow); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java deleted file mode 100644 index 5932f6fbf..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java +++ /dev/null @@ -1,395 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import com.mapbox.mapboxsdk.log.Logger; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -class MapChangeReceiver implements NativeMapView.StateCallback { - - private static final String TAG = "Mbgl-MapChangeReceiver"; - - private final List onCameraWillChangeListenerList = new CopyOnWriteArrayList<>(); - private final List onCameraIsChangingListenerList = new CopyOnWriteArrayList<>(); - private final List onCameraDidChangeListenerList = new CopyOnWriteArrayList<>(); - private final List onWillStartLoadingMapListenerList - = new CopyOnWriteArrayList<>(); - private final List onDidFinishLoadingMapListenerList - = new CopyOnWriteArrayList<>(); - private final List onDidFailLoadingMapListenerList - = new CopyOnWriteArrayList<>(); - private final List onWillStartRenderingFrameList - = new CopyOnWriteArrayList<>(); - private final List onDidFinishRenderingFrameList - = new CopyOnWriteArrayList<>(); - private final List onWillStartRenderingMapListenerList - = new CopyOnWriteArrayList<>(); - private final List onDidFinishRenderingMapListenerList - = new CopyOnWriteArrayList<>(); - private final List onDidBecomeIdleListenerList - = new CopyOnWriteArrayList<>(); - private final List onDidFinishLoadingStyleListenerList - = new CopyOnWriteArrayList<>(); - private final List onSourceChangedListenerList = new CopyOnWriteArrayList<>(); - private final List onStyleImageMissingListenerList - = new CopyOnWriteArrayList<>(); - private final List onCanRemoveUnusedStyleImageListenerList - = new CopyOnWriteArrayList<>(); - - @Override - public void onCameraWillChange(boolean animated) { - try { - if (!onCameraWillChangeListenerList.isEmpty()) { - for (MapView.OnCameraWillChangeListener onCameraWillChangeListener : onCameraWillChangeListenerList) { - onCameraWillChangeListener.onCameraWillChange(animated); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onCameraWillChange", err); - throw err; - } - } - - @Override - public void onCameraIsChanging() { - try { - if (!onCameraIsChangingListenerList.isEmpty()) { - for (MapView.OnCameraIsChangingListener onCameraIsChangingListener : onCameraIsChangingListenerList) { - onCameraIsChangingListener.onCameraIsChanging(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onCameraIsChanging", err); - throw err; - } - } - - @Override - public void onCameraDidChange(boolean animated) { - try { - if (!onCameraDidChangeListenerList.isEmpty()) { - for (MapView.OnCameraDidChangeListener onCameraDidChangeListener : onCameraDidChangeListenerList) { - onCameraDidChangeListener.onCameraDidChange(animated); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onCameraDidChange", err); - throw err; - } - } - - @Override - public void onWillStartLoadingMap() { - try { - if (!onWillStartLoadingMapListenerList.isEmpty()) { - for (MapView.OnWillStartLoadingMapListener onWillStartLoadingMapListener : onWillStartLoadingMapListenerList) { - onWillStartLoadingMapListener.onWillStartLoadingMap(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onWillStartLoadingMap", err); - throw err; - } - } - - @Override - public void onDidFinishLoadingMap() { - try { - if (!onDidFinishLoadingMapListenerList.isEmpty()) { - for (MapView.OnDidFinishLoadingMapListener onDidFinishLoadingMapListener : onDidFinishLoadingMapListenerList) { - onDidFinishLoadingMapListener.onDidFinishLoadingMap(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onDidFinishLoadingMap", err); - throw err; - } - } - - @Override - public void onDidFailLoadingMap(String error) { - try { - if (!onDidFailLoadingMapListenerList.isEmpty()) { - for (MapView.OnDidFailLoadingMapListener onDidFailLoadingMapListener : onDidFailLoadingMapListenerList) { - onDidFailLoadingMapListener.onDidFailLoadingMap(error); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onDidFailLoadingMap", err); - throw err; - } - } - - @Override - public void onWillStartRenderingFrame() { - try { - if (!onWillStartRenderingFrameList.isEmpty()) { - for (MapView.OnWillStartRenderingFrameListener listener : onWillStartRenderingFrameList) { - listener.onWillStartRenderingFrame(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onWillStartRenderingFrame", err); - throw err; - } - } - - @Override - public void onDidFinishRenderingFrame(boolean fully) { - try { - if (!onDidFinishRenderingFrameList.isEmpty()) { - for (MapView.OnDidFinishRenderingFrameListener listener : onDidFinishRenderingFrameList) { - listener.onDidFinishRenderingFrame(fully); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onDidFinishRenderingFrame", err); - throw err; - } - } - - @Override - public void onWillStartRenderingMap() { - try { - if (!onWillStartRenderingMapListenerList.isEmpty()) { - for (MapView.OnWillStartRenderingMapListener listener : onWillStartRenderingMapListenerList) { - listener.onWillStartRenderingMap(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onWillStartRenderingMap", err); - throw err; - } - } - - @Override - public void onDidFinishRenderingMap(boolean fully) { - try { - if (!onDidFinishRenderingMapListenerList.isEmpty()) { - for (MapView.OnDidFinishRenderingMapListener listener : onDidFinishRenderingMapListenerList) { - listener.onDidFinishRenderingMap(fully); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onDidFinishRenderingMap", err); - throw err; - } - } - - @Override - public void onDidBecomeIdle() { - try { - if (!onDidBecomeIdleListenerList.isEmpty()) { - for (MapView.OnDidBecomeIdleListener listener : onDidBecomeIdleListenerList) { - listener.onDidBecomeIdle(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onDidBecomeIdle", err); - throw err; - } - } - - @Override - public void onDidFinishLoadingStyle() { - try { - if (!onDidFinishLoadingStyleListenerList.isEmpty()) { - for (MapView.OnDidFinishLoadingStyleListener listener : onDidFinishLoadingStyleListenerList) { - listener.onDidFinishLoadingStyle(); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onDidFinishLoadingStyle", err); - throw err; - } - } - - @Override - public void onSourceChanged(String sourceId) { - try { - if (!onSourceChangedListenerList.isEmpty()) { - for (MapView.OnSourceChangedListener onSourceChangedListener : onSourceChangedListenerList) { - onSourceChangedListener.onSourceChangedListener(sourceId); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onSourceChanged", err); - throw err; - } - } - - @Override - public void onStyleImageMissing(String imageId) { - try { - if (!onStyleImageMissingListenerList.isEmpty()) { - for (MapView.OnStyleImageMissingListener listener : onStyleImageMissingListenerList) { - listener.onStyleImageMissing(imageId); - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onStyleImageMissing", err); - throw err; - } - } - - @Override - public boolean onCanRemoveUnusedStyleImage(String imageId) { - if (onCanRemoveUnusedStyleImageListenerList.isEmpty()) { - return true; - } - - try { - if (!onCanRemoveUnusedStyleImageListenerList.isEmpty()) { - boolean canRemove = true; - for (MapView.OnCanRemoveUnusedStyleImageListener listener : onCanRemoveUnusedStyleImageListenerList) { - canRemove &= listener.onCanRemoveUnusedStyleImage(imageId); - } - - return canRemove; - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onCanRemoveUnusedStyleImage", err); - throw err; - } - - return true; - } - - void addOnCameraWillChangeListener(MapView.OnCameraWillChangeListener listener) { - onCameraWillChangeListenerList.add(listener); - } - - void removeOnCameraWillChangeListener(MapView.OnCameraWillChangeListener listener) { - onCameraWillChangeListenerList.remove(listener); - } - - void addOnCameraIsChangingListener(MapView.OnCameraIsChangingListener listener) { - onCameraIsChangingListenerList.add(listener); - } - - void removeOnCameraIsChangingListener(MapView.OnCameraIsChangingListener listener) { - onCameraIsChangingListenerList.remove(listener); - } - - void addOnCameraDidChangeListener(MapView.OnCameraDidChangeListener listener) { - onCameraDidChangeListenerList.add(listener); - } - - void removeOnCameraDidChangeListener(MapView.OnCameraDidChangeListener listener) { - onCameraDidChangeListenerList.remove(listener); - } - - void addOnWillStartLoadingMapListener(MapView.OnWillStartLoadingMapListener listener) { - onWillStartLoadingMapListenerList.add(listener); - } - - void removeOnWillStartLoadingMapListener(MapView.OnWillStartLoadingMapListener listener) { - onWillStartLoadingMapListenerList.remove(listener); - } - - void addOnDidFinishLoadingMapListener(MapView.OnDidFinishLoadingMapListener listener) { - onDidFinishLoadingMapListenerList.add(listener); - } - - void removeOnDidFinishLoadingMapListener(MapView.OnDidFinishLoadingMapListener listener) { - onDidFinishLoadingMapListenerList.remove(listener); - } - - void addOnDidFailLoadingMapListener(MapView.OnDidFailLoadingMapListener listener) { - onDidFailLoadingMapListenerList.add(listener); - } - - void removeOnDidFailLoadingMapListener(MapView.OnDidFailLoadingMapListener listener) { - onDidFailLoadingMapListenerList.remove(listener); - } - - void addOnWillStartRenderingFrameListener(MapView.OnWillStartRenderingFrameListener listener) { - onWillStartRenderingFrameList.add(listener); - } - - void removeOnWillStartRenderingFrameListener(MapView.OnWillStartRenderingFrameListener listener) { - onWillStartRenderingFrameList.remove(listener); - } - - void addOnDidFinishRenderingFrameListener(MapView.OnDidFinishRenderingFrameListener listener) { - onDidFinishRenderingFrameList.add(listener); - } - - void removeOnDidFinishRenderingFrameListener(MapView.OnDidFinishRenderingFrameListener listener) { - onDidFinishRenderingFrameList.remove(listener); - } - - void addOnWillStartRenderingMapListener(MapView.OnWillStartRenderingMapListener listener) { - onWillStartRenderingMapListenerList.add(listener); - } - - void removeOnWillStartRenderingMapListener(MapView.OnWillStartRenderingMapListener listener) { - onWillStartRenderingMapListenerList.remove(listener); - } - - void addOnDidFinishRenderingMapListener(MapView.OnDidFinishRenderingMapListener listener) { - onDidFinishRenderingMapListenerList.add(listener); - } - - void removeOnDidFinishRenderingMapListener(MapView.OnDidFinishRenderingMapListener listener) { - onDidFinishRenderingMapListenerList.remove(listener); - } - - void addOnDidBecomeIdleListener(MapView.OnDidBecomeIdleListener listener) { - onDidBecomeIdleListenerList.add(listener); - } - - void removeOnDidBecomeIdleListener(MapView.OnDidBecomeIdleListener listener) { - onDidBecomeIdleListenerList.remove(listener); - } - - void addOnDidFinishLoadingStyleListener(MapView.OnDidFinishLoadingStyleListener listener) { - onDidFinishLoadingStyleListenerList.add(listener); - } - - void removeOnDidFinishLoadingStyleListener(MapView.OnDidFinishLoadingStyleListener listener) { - onDidFinishLoadingStyleListenerList.remove(listener); - } - - void addOnSourceChangedListener(MapView.OnSourceChangedListener listener) { - onSourceChangedListenerList.add(listener); - } - - void removeOnSourceChangedListener(MapView.OnSourceChangedListener listener) { - onSourceChangedListenerList.remove(listener); - } - - void addOnStyleImageMissingListener(MapView.OnStyleImageMissingListener listener) { - onStyleImageMissingListenerList.add(listener); - } - - void removeOnStyleImageMissingListener(MapView.OnStyleImageMissingListener listener) { - onStyleImageMissingListenerList.remove(listener); - } - - void addOnCanRemoveUnusedStyleImageListener(MapView.OnCanRemoveUnusedStyleImageListener listener) { - onCanRemoveUnusedStyleImageListenerList.add(listener); - } - - void removeOnCanRemoveUnusedStyleImageListener(MapView.OnCanRemoveUnusedStyleImageListener listener) { - onCanRemoveUnusedStyleImageListenerList.remove(listener); - } - - void clear() { - onCameraWillChangeListenerList.clear(); - onCameraIsChangingListenerList.clear(); - onCameraDidChangeListenerList.clear(); - onWillStartLoadingMapListenerList.clear(); - onDidFinishLoadingMapListenerList.clear(); - onDidFailLoadingMapListenerList.clear(); - onWillStartRenderingFrameList.clear(); - onDidFinishRenderingFrameList.clear(); - onWillStartRenderingMapListenerList.clear(); - onDidFinishRenderingMapListenerList.clear(); - onDidBecomeIdleListenerList.clear(); - onDidFinishLoadingStyleListenerList.clear(); - onSourceChangedListenerList.clear(); - onStyleImageMissingListenerList.clear(); - onCanRemoveUnusedStyleImageListenerList.clear(); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java deleted file mode 100644 index 8af9f2c6a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapFragment.java +++ /dev/null @@ -1,241 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.app.Fragment; -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import com.mapbox.mapboxsdk.utils.MapFragmentUtils; - -import java.util.ArrayList; -import java.util.List; - -/** - * Fragment wrapper around a map view. - *

- * A Map component in an app. This fragment is the simplest way to place a map in an application. - * It's a wrapper around a view of a map to automatically handle the necessary life cycle needs. - * Being a fragment, this component can be added to an activity's layout or can dynamically be added - * using a FragmentManager. - *

- *

- * To get a reference to the MapView, use {@link #getMapAsync(OnMapReadyCallback)}} - *

- * - * @see #getMapAsync(OnMapReadyCallback) - */ -public final class MapFragment extends Fragment implements OnMapReadyCallback { - - private final List mapReadyCallbackList = new ArrayList<>(); - private OnMapViewReadyCallback mapViewReadyCallback; - private MapboxMap mapboxMap; - private MapView map; - - /** - * Creates a default MapFragment instance - * - * @return MapFragment instantiated - */ - public static MapFragment newInstance() { - return new MapFragment(); - } - - /** - * Creates a MapFragment instance - * - * @param mapboxMapOptions The configuration options to be used. - * @return MapFragment instantiated. - */ - @NonNull - public static MapFragment newInstance(@Nullable MapboxMapOptions mapboxMapOptions) { - MapFragment mapFragment = new MapFragment(); - mapFragment.setArguments(MapFragmentUtils.createFragmentArgs(mapboxMapOptions)); - return mapFragment; - } - - /** - * Called when this fragment is inflated, parses XML tag attributes. - * - * @param context The context inflating this fragment. - * @param attrs The XML tag attributes. - * @param savedInstanceState The saved instance state for the map fragment. - */ - @Override - public void onInflate(@NonNull Context context, AttributeSet attrs, Bundle savedInstanceState) { - super.onInflate(context, attrs, savedInstanceState); - setArguments(MapFragmentUtils.createFragmentArgs(MapboxMapOptions.createFromAttributes(context, attrs))); - } - - /** - * Called when the context attaches to this fragment. - * - * @param context the context attaching - */ - @Override - public void onAttach(Context context) { - super.onAttach(context); - if (context instanceof OnMapViewReadyCallback) { - mapViewReadyCallback = (OnMapViewReadyCallback) context; - } - } - - /** - * Creates the fragment view hierarchy. - * - * @param inflater Inflater used to inflate content. - * @param container The parent layout for the map fragment. - * @param savedInstanceState The saved instance state for the map fragment. - * @return The view created - */ - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - super.onCreateView(inflater, container, savedInstanceState); - Context context = inflater.getContext(); - map = new MapView(context, MapFragmentUtils.resolveArgs(context, getArguments())); - return map; - } - - /** - * Called when the fragment view hierarchy is created. - * - * @param view The content view of the fragment - * @param savedInstanceState The saved instance state of the fragment - */ - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - map.onCreate(savedInstanceState); - map.getMapAsync(this); - - // notify listeners about mapview creation - if (mapViewReadyCallback != null) { - mapViewReadyCallback.onMapViewReady(map); - } - } - - /** - * Called when the style of the map has successfully loaded. - * - * @param mapboxMap The public api controller of the map - */ - @Override - public void onMapReady(@NonNull MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - for (OnMapReadyCallback onMapReadyCallback : mapReadyCallbackList) { - onMapReadyCallback.onMapReady(mapboxMap); - } - } - - /** - * Called when the fragment is visible for the users. - */ - @Override - public void onStart() { - super.onStart(); - map.onStart(); - } - - /** - * Called when the fragment is ready to be interacted with. - */ - @Override - public void onResume() { - super.onResume(); - map.onResume(); - } - - /** - * Called when the fragment is pausing. - */ - @Override - public void onPause() { - super.onPause(); - map.onPause(); - } - - /** - * Called when the fragment state needs to be saved. - * - * @param outState The saved state - */ - @Override - public void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - if (map != null && !map.isDestroyed()) { - map.onSaveInstanceState(outState); - } - } - - /** - * Called when the fragment is no longer visible for the user. - */ - @Override - public void onStop() { - super.onStop(); - map.onStop(); - } - - /** - * Called when the fragment receives onLowMemory call from the hosting Activity. - */ - @Override - public void onLowMemory() { - super.onLowMemory(); - if (map != null && !map.isDestroyed()) { - map.onLowMemory(); - } - } - - /** - * Called when the fragment is view hiearchy is being destroyed. - */ - @Override - public void onDestroyView() { - super.onDestroyView(); - map.onDestroy(); - } - - /** - * Called when the fragment is destroyed. - */ - @Override - public void onDestroy() { - super.onDestroy(); - mapReadyCallbackList.clear(); - } - - /** - * Sets a callback object which will be triggered when the MapboxMap instance is ready to be used. - * - * @param onMapReadyCallback The callback to be invoked. - */ - public void getMapAsync(@NonNull final OnMapReadyCallback onMapReadyCallback) { - if (mapboxMap == null) { - mapReadyCallbackList.add(onMapReadyCallback); - } else { - onMapReadyCallback.onMapReady(mapboxMap); - } - } - - /** - * Callback to be invoked when the map fragment has inflated its MapView. - *

- * To use this interface the context hosting the fragment must implement this interface. - * That instance will be set as part of Fragment#onAttach(Context context). - *

- */ - public interface OnMapViewReadyCallback { - - /** - * Called when the map has been created. - * - * @param mapView The created mapview - */ - void onMapViewReady(MapView mapView); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java deleted file mode 100644 index 90e3934f7..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapGestureDetector.java +++ /dev/null @@ -1,1140 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.content.Context; -import android.content.res.Resources; -import android.graphics.PointF; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.InputDevice; -import android.view.MotionEvent; -import android.view.animation.DecelerateInterpolator; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.android.gestures.MultiFingerTapGestureDetector; -import com.mapbox.android.gestures.RotateGestureDetector; -import com.mapbox.android.gestures.ShoveGestureDetector; -import com.mapbox.android.gestures.StandardGestureDetector; -import com.mapbox.android.gestures.StandardScaleGestureDetector; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.utils.MathUtils; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.concurrent.CopyOnWriteArrayList; - -import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAXIMUM_ANGULAR_VELOCITY; -import static com.mapbox.mapboxsdk.constants.MapboxConstants.MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE; -import static com.mapbox.mapboxsdk.constants.MapboxConstants.QUICK_ZOOM_MAX_ZOOM_CHANGE; -import static com.mapbox.mapboxsdk.constants.MapboxConstants.ROTATE_VELOCITY_RATIO_THRESHOLD; -import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER; -import static com.mapbox.mapboxsdk.constants.MapboxConstants.SCALE_VELOCITY_RATIO_THRESHOLD; -import static com.mapbox.mapboxsdk.constants.MapboxConstants.ZOOM_RATE; -import static com.mapbox.mapboxsdk.maps.MapboxMap.OnCameraMoveStartedListener.REASON_API_GESTURE; -import static com.mapbox.mapboxsdk.utils.MathUtils.normalize; - -/** - * Manages gestures events on a MapView. - */ -final class MapGestureDetector { - - private final Transform transform; - private final Projection projection; - private final UiSettings uiSettings; - private final AnnotationManager annotationManager; - private final CameraChangeDispatcher cameraChangeDispatcher; - - // new map touch API - private final CopyOnWriteArrayList onMapClickListenerList - = new CopyOnWriteArrayList<>(); - - private final CopyOnWriteArrayList onMapLongClickListenerList - = new CopyOnWriteArrayList<>(); - - private final CopyOnWriteArrayList onFlingListenerList - = new CopyOnWriteArrayList<>(); - - private final CopyOnWriteArrayList onMoveListenerList - = new CopyOnWriteArrayList<>(); - - private final CopyOnWriteArrayList onRotateListenerList - = new CopyOnWriteArrayList<>(); - - private final CopyOnWriteArrayList onScaleListenerList - = new CopyOnWriteArrayList<>(); - - private final CopyOnWriteArrayList onShoveListenerList - = new CopyOnWriteArrayList<>(); - - /** - * User-set focal point. - */ - @Nullable - private PointF constantFocalPoint; - - @NonNull - private PointF doubleTapFocalPoint = new PointF(); - - private AndroidGesturesManager gesturesManager; - - private Animator scaleAnimator; - private Animator rotateAnimator; - private final List scheduledAnimators = new ArrayList<>(); - - /** - * Cancels scheduled velocity animations if user doesn't lift fingers within - * {@link MapboxConstants#SCHEDULED_ANIMATION_TIMEOUT} - */ - @NonNull - private Handler animationsTimeoutHandler = new Handler(); - - private boolean doubleTapRegistered; - - MapGestureDetector(@Nullable Context context, Transform transform, Projection projection, UiSettings uiSettings, - AnnotationManager annotationManager, CameraChangeDispatcher cameraChangeDispatcher) { - this.annotationManager = annotationManager; - this.transform = transform; - this.projection = projection; - this.uiSettings = uiSettings; - this.cameraChangeDispatcher = cameraChangeDispatcher; - - // Checking for context != null for testing purposes - if (context != null) { - // Initialize gestures manager - AndroidGesturesManager androidGesturesManager = new AndroidGesturesManager(context); - initializeGesturesManager(androidGesturesManager, true); - - // Initialize gesture listeners - initializeGestureListeners(context, true); - } - } - - private void initializeGestureListeners(@NonNull Context context, boolean attachDefaultListeners) { - if (attachDefaultListeners) { - StandardGestureListener standardGestureListener = new StandardGestureListener( - context.getResources().getDimension( - com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold)); - MoveGestureListener moveGestureListener = new MoveGestureListener(); - ScaleGestureListener scaleGestureListener = new ScaleGestureListener( - context.getResources().getDimension(R.dimen.mapbox_density_constant), - context.getResources().getDimension(R.dimen.mapbox_minimum_scale_speed), - context.getResources().getDimension(R.dimen.mapbox_minimum_angled_scale_speed), - context.getResources().getDimension(R.dimen.mapbox_minimum_scale_velocity) - ); - RotateGestureListener rotateGestureListener = new RotateGestureListener( - context.getResources().getDimension(R.dimen.mapbox_minimum_scale_span_when_rotating), - context.getResources().getDimension(R.dimen.mapbox_density_constant), - context.getResources().getDimension(R.dimen.mapbox_angular_velocity_multiplier), - context.getResources().getDimension(R.dimen.mapbox_minimum_angular_velocity), - context.getResources().getDimension( - com.mapbox.android.gestures.R.dimen.mapbox_defaultScaleSpanSinceStartThreshold)); - ShoveGestureListener shoveGestureListener = new ShoveGestureListener(); - TapGestureListener tapGestureListener = new TapGestureListener(); - - gesturesManager.setStandardGestureListener(standardGestureListener); - gesturesManager.setMoveGestureListener(moveGestureListener); - gesturesManager.setStandardScaleGestureListener(scaleGestureListener); - gesturesManager.setRotateGestureListener(rotateGestureListener); - gesturesManager.setShoveGestureListener(shoveGestureListener); - gesturesManager.setMultiFingerTapGestureListener(tapGestureListener); - } - } - - private void initializeGesturesManager(@NonNull AndroidGesturesManager androidGesturesManager, - boolean setDefaultMutuallyExclusives) { - if (setDefaultMutuallyExclusives) { - Set shoveScaleSet = new HashSet<>(); - shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); - shoveScaleSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); - - Set shoveRotateSet = new HashSet<>(); - shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_SHOVE); - shoveRotateSet.add(AndroidGesturesManager.GESTURE_TYPE_ROTATE); - - Set ScaleLongPressSet = new HashSet<>(); - ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_SCALE); - ScaleLongPressSet.add(AndroidGesturesManager.GESTURE_TYPE_LONG_PRESS); - - androidGesturesManager.setMutuallyExclusiveGestures(shoveScaleSet, shoveRotateSet, ScaleLongPressSet); - } - - gesturesManager = androidGesturesManager; - gesturesManager.getRotateGestureDetector().setAngleThreshold(3f); - } - - /** - * Set the gesture focal point. - *

- * This is the center point used for calculate transformations from gestures, value is - * overridden if end user provides his own through {@link UiSettings#setFocalPoint(PointF)}. - *

- * - * @param focalPoint the center point for gestures - */ - void setFocalPoint(@Nullable PointF focalPoint) { - if (focalPoint == null) { - // resetting focal point, - if (uiSettings.getFocalPoint() != null) { - // using user provided one to reset - focalPoint = uiSettings.getFocalPoint(); - } - } - this.constantFocalPoint = focalPoint; - } - - /** - * Called when user touches the screen, all positions are absolute. - *

- * Forwards event to the related gesture detectors. - *

- * - * @param motionEvent the MotionEvent - * @return True if touch event is handled - */ - boolean onTouchEvent(@Nullable MotionEvent motionEvent) { - // Framework can return null motion events in edge cases #9432 - if (motionEvent == null) { - return false; - } - - // Check and ignore non touch or left clicks - if ((motionEvent.getButtonState() != 0) && (motionEvent.getButtonState() != MotionEvent.BUTTON_PRIMARY)) { - return false; - } - - if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { - cancelAnimators(); - transform.setGestureInProgress(true); - } - - boolean result = gesturesManager.onTouchEvent(motionEvent); - - switch (motionEvent.getActionMasked()) { - case MotionEvent.ACTION_POINTER_DOWN: - doubleTapFinished(); - break; - - case MotionEvent.ACTION_UP: - doubleTapFinished(); - transform.setGestureInProgress(false); - - if (!scheduledAnimators.isEmpty()) { - // Start all awaiting velocity animations - animationsTimeoutHandler.removeCallbacksAndMessages(null); - for (Animator animator : scheduledAnimators) { - animator.start(); - } - scheduledAnimators.clear(); - } - break; - - case MotionEvent.ACTION_CANCEL: - scheduledAnimators.clear(); - transform.setGestureInProgress(false); - doubleTapFinished(); - break; - } - - return result; - } - - void cancelAnimators() { - animationsTimeoutHandler.removeCallbacksAndMessages(null); - scheduledAnimators.clear(); - - cancelAnimator(scaleAnimator); - cancelAnimator(rotateAnimator); - - dispatchCameraIdle(); - } - - private void cancelAnimator(@Nullable Animator animator) { - if (animator != null && animator.isStarted()) { - animator.cancel(); - } - } - - /** - * Posted on main thread with {@link #animationsTimeoutHandler}. Cancels all scheduled animators if needed. - */ - @NonNull - private final Runnable cancelAnimatorsRunnable = new Runnable() { - @Override - public void run() { - cancelAnimators(); - } - }; - - /** - * Schedules a velocity animator to be executed when user lifts fingers, - * unless canceled by the {@link #cancelAnimatorsRunnable}. - * - * @param animator animator ot be scheduled - */ - private void scheduleAnimator(Animator animator) { - scheduledAnimators.add(animator); - animationsTimeoutHandler.removeCallbacksAndMessages(null); - animationsTimeoutHandler.postDelayed(cancelAnimatorsRunnable, MapboxConstants.SCHEDULED_ANIMATION_TIMEOUT); - } - - /** - * Called for events that don't fit the other handlers. - *

- * Examples of such events are mouse scroll events, mouse moves, joystick & trackpad. - *

- * - * @param event The MotionEvent occurred - * @return True is the event is handled - */ - boolean onGenericMotionEvent(MotionEvent event) { - // Mouse events - // if (event.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) { // this is not available before API 18 - if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.SOURCE_CLASS_POINTER) { - // Choose the action - switch (event.getActionMasked()) { - // Mouse scrolls - case MotionEvent.ACTION_SCROLL: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Get the vertical scroll amount, one click = 1 - float scrollDist = event.getAxisValue(MotionEvent.AXIS_VSCROLL); - - // Scale the map by the appropriate power of two factor - transform.zoomBy(scrollDist, new PointF(event.getX(), event.getY())); - - return true; - - default: - // We are not interested in this event - return false; - } - } - - // We are not interested in this event - return false; - } - - private final class StandardGestureListener extends StandardGestureDetector.SimpleStandardOnGestureListener { - private final float doubleTapMovementThreshold; - - StandardGestureListener(float doubleTapMovementThreshold) { - this.doubleTapMovementThreshold = doubleTapMovementThreshold; - } - - @Override - public boolean onDown(MotionEvent motionEvent) { - return true; - } - - @Override - public boolean onSingleTapUp(MotionEvent motionEvent) { - transform.cancelTransitions(); - return true; - } - - @Override - public boolean onSingleTapConfirmed(MotionEvent motionEvent) { - PointF tapPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - boolean tapHandled = annotationManager.onTap(tapPoint); - - if (!tapHandled) { - if (uiSettings.isDeselectMarkersOnTap()) { - // deselect any selected marker - annotationManager.deselectMarkers(); - } - - notifyOnMapClickListeners(tapPoint); - } - - return true; - } - - @Override - public boolean onDoubleTapEvent(MotionEvent motionEvent) { - int action = motionEvent.getActionMasked(); - if (action == MotionEvent.ACTION_DOWN) { - doubleTapFocalPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - doubleTapStarted(); - } - - if (motionEvent.getActionMasked() == MotionEvent.ACTION_UP) { - float diffX = Math.abs(motionEvent.getX() - doubleTapFocalPoint.x); - float diffY = Math.abs(motionEvent.getY() - doubleTapFocalPoint.y); - if (diffX > doubleTapMovementThreshold || diffY > doubleTapMovementThreshold) { - // Ignore double-tap event because we've started the quick-zoom. See #14013. - return false; - } - - if (!uiSettings.isZoomGesturesEnabled() || !uiSettings.isDoubleTapGesturesEnabled()) { - return false; - } - - // Single finger double tap - if (constantFocalPoint != null) { - // User provided focal point - doubleTapFocalPoint = constantFocalPoint; - } - - zoomInAnimated(doubleTapFocalPoint, false); - - return true; - } - - return super.onDoubleTapEvent(motionEvent); - } - - @Override - public void onLongPress(MotionEvent motionEvent) { - PointF longClickPoint = new PointF(motionEvent.getX(), motionEvent.getY()); - notifyOnMapLongClickListeners(longClickPoint); - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { - if (!uiSettings.isScrollGesturesEnabled()) { - // don't allow a fling if scroll is disabled - return false; - } - - notifyOnFlingListeners(); - - if (!uiSettings.isFlingVelocityAnimationEnabled()) { - return false; - } - - float screenDensity = uiSettings.getPixelRatio(); - - // calculate velocity vector for xy dimensions, independent from screen size - double velocityXY = Math.hypot(velocityX / screenDensity, velocityY / screenDensity); - if (velocityXY < MapboxConstants.VELOCITY_THRESHOLD_IGNORE_FLING) { - // ignore short flings, these can occur when other gestures just have finished executing - return false; - } - - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - // tilt results in a bigger translation, limiting input for #5281 - double tilt = transform.getTilt(); - double tiltFactor = 1.5 + ((tilt != 0) ? (tilt / 10) : 0); - double offsetX = velocityX / tiltFactor / screenDensity; - double offsetY = velocityY / tiltFactor / screenDensity; - - // calculate animation time based on displacement - long animationTime = (long) (velocityXY / 7 / tiltFactor + MapboxConstants.ANIMATION_DURATION_FLING_BASE); - - // update transformation - transform.moveBy(offsetX, offsetY, animationTime); - - return true; - } - } - - private void doubleTapStarted() { - // disable the move detector in preparation for the quickzoom, - // so that we don't move the map's center slightly before the quickzoom is started (see #14227) - gesturesManager.getMoveGestureDetector().setEnabled(false); - doubleTapRegistered = true; - } - - private void doubleTapFinished() { - if (doubleTapRegistered) { - // re-enable the move detector in case of double tap - gesturesManager.getMoveGestureDetector().setEnabled(true); - doubleTapRegistered = false; - } - } - - private final class MoveGestureListener extends MoveGestureDetector.SimpleOnMoveGestureListener { - @Override - public boolean onMoveBegin(@NonNull MoveGestureDetector detector) { - if (!uiSettings.isScrollGesturesEnabled()) { - return false; - } - - cancelTransitionsIfRequired(); - notifyOnMoveBeginListeners(detector); - return true; - } - - @Override - public boolean onMove(@NonNull MoveGestureDetector detector, float distanceX, float distanceY) { - // first move event is often delivered with no displacement - if (distanceX != 0 || distanceY != 0) { - // dispatching camera start event only when the movement actually occurred - cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - - // Scroll the map - transform.moveBy(-distanceX, -distanceY, 0 /*no duration*/); - - notifyOnMoveListeners(detector); - } - return true; - } - - @Override - public void onMoveEnd(@NonNull MoveGestureDetector detector, float velocityX, float velocityY) { - dispatchCameraIdle(); - notifyOnMoveEndListeners(detector); - } - } - - private final class ScaleGestureListener extends StandardScaleGestureDetector.SimpleStandardOnScaleGestureListener { - - private final float minimumGestureSpeed; - private final float minimumAngledGestureSpeed; - private final float minimumVelocity; - private final double scaleVelocityRatioThreshold; - private boolean quickZoom; - private float spanSinceLast; - private double screenHeight; - private double startZoom; - - ScaleGestureListener(double densityMultiplier, float minimumGestureSpeed, float minimumAngledGestureSpeed, - float minimumVelocity) { - this.minimumGestureSpeed = minimumGestureSpeed; - this.minimumAngledGestureSpeed = minimumAngledGestureSpeed; - this.minimumVelocity = minimumVelocity; - this.scaleVelocityRatioThreshold = SCALE_VELOCITY_RATIO_THRESHOLD * densityMultiplier; - } - - @Override - public boolean onScaleBegin(@NonNull StandardScaleGestureDetector detector) { - quickZoom = detector.getPointersCount() == 1; - - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - if (quickZoom) { - if (!uiSettings.isQuickZoomGesturesEnabled()) { - return false; - } - // re-try disabling the move detector in case double tap has been interrupted before quickzoom started - gesturesManager.getMoveGestureDetector().setEnabled(false); - } else { - if (detector.getPreviousSpan() > 0) { - float currSpan = detector.getCurrentSpan(); - float prevSpan = detector.getPreviousSpan(); - double currTime = detector.getCurrentEvent().getEventTime(); - double prevTime = detector.getPreviousEvent().getEventTime(); - if (currTime == prevTime) { - return false; - } - double speed = Math.abs(currSpan - prevSpan) / (currTime - prevTime); - if (speed < minimumGestureSpeed) { - // do not scale if the minimal gesture speed is not met - return false; - } else if (!gesturesManager.getRotateGestureDetector().isInProgress()) { - float rotationDeltaSinceLast = gesturesManager.getRotateGestureDetector().getDeltaSinceLast(); - if (Math.abs(rotationDeltaSinceLast) > 0.4 && speed < minimumAngledGestureSpeed) { - // do not scale in case we're preferring to start rotation - return false; - } - - if (uiSettings.isDisableRotateWhenScaling()) { - // disable rotate gesture when scale is detected first - gesturesManager.getRotateGestureDetector().setEnabled(false); - } - } - } else { - return false; - } - } - - screenHeight = Resources.getSystem().getDisplayMetrics().heightPixels; - startZoom = transform.getRawZoom(); - - cancelTransitionsIfRequired(); - - notifyOnScaleBeginListeners(detector); - - spanSinceLast = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan()); - - return true; - } - - @Override - public boolean onScale(@NonNull StandardScaleGestureDetector detector) { - // dispatching camera start event only when the movement actually occurred - cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - - PointF focalPoint = getScaleFocalPoint(detector); - if (quickZoom) { - double pixelDeltaChange = Math.abs(detector.getCurrentEvent().getY() - doubleTapFocalPoint.y); - boolean zoomedOut = detector.getCurrentEvent().getY() < doubleTapFocalPoint.y; - - // normalize the pixel delta change, ranging from 0 to screen height, to a constant zoom change range - double normalizedDeltaChange = normalize(pixelDeltaChange, 0, screenHeight, 0, QUICK_ZOOM_MAX_ZOOM_CHANGE); - - // calculate target zoom and adjust for a multiplier - double targetZoom = (zoomedOut ? startZoom - normalizedDeltaChange : startZoom + normalizedDeltaChange); - targetZoom *= uiSettings.getZoomRate(); - - transform.setZoom(targetZoom, focalPoint); - } else { - double zoomBy = - (Math.log(detector.getScaleFactor()) / Math.log(Math.PI / 2)) * ZOOM_RATE * uiSettings.getZoomRate(); - transform.zoomBy(zoomBy, focalPoint); - } - - notifyOnScaleListeners(detector); - - spanSinceLast = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan()); - - return true; - } - - @Override - public void onScaleEnd(@NonNull StandardScaleGestureDetector detector, float velocityX, float velocityY) { - if (quickZoom) { - // re-enabled the move detector if the quickzoom happened - gesturesManager.getMoveGestureDetector().setEnabled(true); - } else { - // re-enable rotation in case it's been disabled - gesturesManager.getRotateGestureDetector().setEnabled(true); - } - - notifyOnScaleEndListeners(detector); - - float velocityXY = Math.abs(velocityX) + Math.abs(velocityY); - - if (!uiSettings.isScaleVelocityAnimationEnabled() - || velocityXY < minimumVelocity - || spanSinceLast / velocityXY < scaleVelocityRatioThreshold) { - // notifying listeners that camera is idle only if there is no follow-up animation - dispatchCameraIdle(); - return; - } - - double zoomAddition = calculateScale(velocityXY, detector.isScalingOut()); - double currentZoom = transform.getRawZoom(); - PointF focalPoint = getScaleFocalPoint(detector); - // (log(x + 1 / e^2) + 2) * 150, x=0 to 2.5 (MapboxConstants#MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE) - long animationTime = (long) ((Math.log((Math.abs(zoomAddition)) + 1 / Math.pow(Math.E, 2)) + 2) - * SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER); - scaleAnimator = createScaleAnimator(currentZoom, zoomAddition, focalPoint, animationTime); - scheduleAnimator(scaleAnimator); - } - - @NonNull - private PointF getScaleFocalPoint(@NonNull StandardScaleGestureDetector detector) { - if (constantFocalPoint != null) { - // around user provided focal point - return constantFocalPoint; - } else if (quickZoom) { - // around center - return new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - } else { - // around gesture - return detector.getFocalPoint(); - } - } - - private double calculateScale(double velocityXY, boolean isScalingOut) { - double zoomAddition = velocityXY * MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE * 1e-4; - zoomAddition = MathUtils.clamp(zoomAddition, 0, MAX_ABSOLUTE_SCALE_VELOCITY_CHANGE); - if (isScalingOut) { - zoomAddition = -zoomAddition; - } - return zoomAddition; - } - } - - private final class RotateGestureListener extends RotateGestureDetector.SimpleOnRotateGestureListener { - private final float minimumScaleSpanWhenRotating; - private final float angularVelocityMultiplier; - private final float minimumAngularVelocity; - private final double rotateVelocityRatioThreshold; - private final float defaultSpanSinceStartThreshold; - - RotateGestureListener(float minimumScaleSpanWhenRotating, double densityMultiplier, float angularVelocityMultiplier, - float minimumAngularVelocity, float defaultSpanSinceStartThreshold) { - this.minimumScaleSpanWhenRotating = minimumScaleSpanWhenRotating; - this.angularVelocityMultiplier = angularVelocityMultiplier; - this.minimumAngularVelocity = minimumAngularVelocity; - this.rotateVelocityRatioThreshold = ROTATE_VELOCITY_RATIO_THRESHOLD * densityMultiplier; - this.defaultSpanSinceStartThreshold = defaultSpanSinceStartThreshold; - } - - @Override - public boolean onRotateBegin(@NonNull RotateGestureDetector detector) { - if (!uiSettings.isRotateGesturesEnabled()) { - return false; - } - - float deltaSinceLast = Math.abs(detector.getDeltaSinceLast()); - double currTime = detector.getCurrentEvent().getEventTime(); - double prevTime = detector.getPreviousEvent().getEventTime(); - if (currTime == prevTime) { - return false; - } - double speed = deltaSinceLast / (currTime - prevTime); - float deltaSinceStart = Math.abs(detector.getDeltaSinceStart()); - - // adjust the responsiveness of a rotation gesture - the higher the speed, the bigger the threshold - if (speed < 0.04 - || (speed > 0.07 && deltaSinceStart < 5) - || (speed > 0.15 && deltaSinceStart < 7) - || (speed > 0.5 && deltaSinceStart < 15) - ) { - return false; - } - - if (uiSettings.isIncreaseScaleThresholdWhenRotating()) { - // when rotation starts, interrupting scale and increasing the threshold - // to make rotation without scaling easier - gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(minimumScaleSpanWhenRotating); - gesturesManager.getStandardScaleGestureDetector().interrupt(); - } - - cancelTransitionsIfRequired(); - - notifyOnRotateBeginListeners(detector); - - return true; - } - - @Override - public boolean onRotate(@NonNull RotateGestureDetector detector, float rotationDegreesSinceLast, - float rotationDegreesSinceFirst) { - // dispatching camera start event only when the movement actually occurred - cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - - // Calculate map bearing value - double bearing = transform.getRawBearing() + rotationDegreesSinceLast; - - // Rotate the map - PointF focalPoint = getRotateFocalPoint(detector); - transform.setBearing(bearing, focalPoint.x, focalPoint.y); - - notifyOnRotateListeners(detector); - - return true; - } - - @Override - public void onRotateEnd(@NonNull RotateGestureDetector detector, float velocityX, - float velocityY, float angularVelocity) { - if (uiSettings.isIncreaseScaleThresholdWhenRotating()) { - // resetting default scale threshold values - gesturesManager.getStandardScaleGestureDetector().setSpanSinceStartThreshold(defaultSpanSinceStartThreshold); - } - - notifyOnRotateEndListeners(detector); - - angularVelocity = angularVelocity * angularVelocityMultiplier; - angularVelocity = MathUtils.clamp(angularVelocity, -MAXIMUM_ANGULAR_VELOCITY, MAXIMUM_ANGULAR_VELOCITY); - - float velocityXY = Math.abs(velocityX) + Math.abs(velocityY); - float delta = Math.abs(detector.getDeltaSinceLast()); - double ratio = delta / velocityXY; - - if (!uiSettings.isRotateVelocityAnimationEnabled() - || Math.abs(angularVelocity) < minimumAngularVelocity - || (gesturesManager.getStandardScaleGestureDetector().isInProgress() && ratio < rotateVelocityRatioThreshold)) { - // notifying listeners that camera is idle only if there is no follow-up animation - dispatchCameraIdle(); - return; - } - - long animationTime = (long) ((Math.log((Math.abs(angularVelocity)) + 1 / Math.pow(Math.E, 2)) + 2) - * SCALE_VELOCITY_ANIMATION_DURATION_MULTIPLIER); - - PointF focalPoint = getRotateFocalPoint(detector); - rotateAnimator = createRotateAnimator(angularVelocity, animationTime, focalPoint); - scheduleAnimator(rotateAnimator); - } - - @NonNull - private PointF getRotateFocalPoint(@NonNull RotateGestureDetector detector) { - if (constantFocalPoint != null) { - // User provided focal point - return constantFocalPoint; - } else { - // around gesture - return detector.getFocalPoint(); - } - } - - private Animator createRotateAnimator(float angularVelocity, long animationTime, - @NonNull final PointF animationFocalPoint) { - ValueAnimator animator = ValueAnimator.ofFloat(angularVelocity, 0f); - animator.setDuration(animationTime); - animator.setInterpolator(new DecelerateInterpolator()); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(@NonNull ValueAnimator animation) { - transform.setBearing( - transform.getRawBearing() + (float) animation.getAnimatedValue(), - animationFocalPoint.x, animationFocalPoint.y, - 0L - ); - } - }); - - animator.addListener(new AnimatorListenerAdapter() { - - @Override - public void onAnimationStart(Animator animation) { - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } - - @Override - public void onAnimationCancel(Animator animation) { - transform.cancelTransitions(); - } - - @Override - public void onAnimationEnd(Animator animation) { - dispatchCameraIdle(); - } - }); - - return animator; - } - } - - private final class ShoveGestureListener extends ShoveGestureDetector.SimpleOnShoveGestureListener { - @Override - public boolean onShoveBegin(@NonNull ShoveGestureDetector detector) { - if (!uiSettings.isTiltGesturesEnabled()) { - return false; - } - - cancelTransitionsIfRequired(); - - // disabling move gesture during shove - gesturesManager.getMoveGestureDetector().setEnabled(false); - - notifyOnShoveBeginListeners(detector); - - return true; - } - - @Override - public boolean onShove(@NonNull ShoveGestureDetector detector, - float deltaPixelsSinceLast, float deltaPixelsSinceStart) { - // dispatching camera start event only when the movement actually occurred - cameraChangeDispatcher.onCameraMoveStarted(CameraChangeDispatcher.REASON_API_GESTURE); - - // Get tilt value (scale and clamp) - double pitch = transform.getTilt(); - pitch -= MapboxConstants.SHOVE_PIXEL_CHANGE_FACTOR * deltaPixelsSinceLast; - pitch = MathUtils.clamp(pitch, MapboxConstants.MINIMUM_TILT, MapboxConstants.MAXIMUM_TILT); - - // Tilt the map - transform.setTilt(pitch); - - notifyOnShoveListeners(detector); - - return true; - } - - @Override - public void onShoveEnd(@NonNull ShoveGestureDetector detector, float velocityX, float velocityY) { - dispatchCameraIdle(); - - // re-enabling move gesture - gesturesManager.getMoveGestureDetector().setEnabled(true); - - notifyOnShoveEndListeners(detector); - } - } - - private final class TapGestureListener implements MultiFingerTapGestureDetector.OnMultiFingerTapGestureListener { - @Override - public boolean onMultiFingerTap(@NonNull MultiFingerTapGestureDetector detector, int pointersCount) { - if (!uiSettings.isZoomGesturesEnabled() || pointersCount != 2) { - return false; - } - - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - - PointF zoomFocalPoint; - // Single finger double tap - if (constantFocalPoint != null) { - // User provided focal point - zoomFocalPoint = constantFocalPoint; - } else { - // Zoom in on gesture - zoomFocalPoint = detector.getFocalPoint(); - } - - zoomOutAnimated(zoomFocalPoint, false); - - return true; - } - } - - private Animator createScaleAnimator(double currentZoom, double zoomAddition, - @NonNull final PointF animationFocalPoint, long animationTime) { - ValueAnimator animator = ValueAnimator.ofFloat((float) currentZoom, (float) (currentZoom + zoomAddition)); - animator.setDuration(animationTime); - animator.setInterpolator(new DecelerateInterpolator()); - animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - - @Override - public void onAnimationUpdate(@NonNull ValueAnimator animation) { - transform.setZoom((Float) animation.getAnimatedValue(), animationFocalPoint); - } - }); - - animator.addListener(new AnimatorListenerAdapter() { - - @Override - public void onAnimationStart(Animator animation) { - transform.cancelTransitions(); - cameraChangeDispatcher.onCameraMoveStarted(REASON_API_GESTURE); - } - - @Override - public void onAnimationCancel(Animator animation) { - transform.cancelTransitions(); - } - - @Override - public void onAnimationEnd(Animator animation) { - dispatchCameraIdle(); - } - }); - return animator; - } - - /** - * Zoom in by 1. - * - * @param zoomFocalPoint focal point of zoom animation - * @param runImmediately if true, animation will be started right away, otherwise it will wait until - * {@link MotionEvent#ACTION_UP} is registered. - */ - void zoomInAnimated(@NonNull PointF zoomFocalPoint, boolean runImmediately) { - zoomAnimated(true, zoomFocalPoint, runImmediately); - } - - /** - * Zoom out by 1. - * - * @param zoomFocalPoint focal point of zoom animation - * @param runImmediately if true, animation will be started right away, otherwise it will wait until - * {@link MotionEvent#ACTION_UP} is registered. - */ - void zoomOutAnimated(@NonNull PointF zoomFocalPoint, boolean runImmediately) { - zoomAnimated(false, zoomFocalPoint, runImmediately); - } - - private void zoomAnimated(boolean zoomIn, @NonNull PointF zoomFocalPoint, boolean runImmediately) { - //canceling here as well, because when using a button it will not be canceled automatically by onDown() - cancelAnimator(scaleAnimator); - - double currentZoom = transform.getRawZoom(); - scaleAnimator = createScaleAnimator( - currentZoom, - zoomIn ? 1 : -1, - zoomFocalPoint, - MapboxConstants.ANIMATION_DURATION); - if (runImmediately) { - scaleAnimator.start(); - } else { - scheduleAnimator(scaleAnimator); - } - } - - private void dispatchCameraIdle() { - // we need to dispatch camera idle callback only if there is no other gestures in progress - if (noGesturesInProgress()) { - // invalidate the camera position, so that it's valid when fetched from the #onIdle event - // and doesn't rely on the last frame being rendered - transform.invalidateCameraPosition(); - cameraChangeDispatcher.onCameraIdle(); - } - } - - private void cancelTransitionsIfRequired() { - // we need to cancel core transitions only if there is no started gesture yet - if (noGesturesInProgress()) { - transform.cancelTransitions(); - } - } - - private boolean noGesturesInProgress() { - return (!uiSettings.isScrollGesturesEnabled() || !gesturesManager.getMoveGestureDetector().isInProgress()) - && (!uiSettings.isZoomGesturesEnabled() || !gesturesManager.getStandardScaleGestureDetector().isInProgress()) - && (!uiSettings.isRotateGesturesEnabled() || !gesturesManager.getRotateGestureDetector().isInProgress()) - && (!uiSettings.isTiltGesturesEnabled() || !gesturesManager.getShoveGestureDetector().isInProgress()); - } - - void notifyOnMapClickListeners(@NonNull PointF tapPoint) { - for (MapboxMap.OnMapClickListener listener : onMapClickListenerList) { - if (listener.onMapClick(projection.fromScreenLocation(tapPoint))) { - return; - } - } - } - - void notifyOnMapLongClickListeners(@NonNull PointF longClickPoint) { - for (MapboxMap.OnMapLongClickListener listener : onMapLongClickListenerList) { - if (listener.onMapLongClick(projection.fromScreenLocation(longClickPoint))) { - return; - } - } - } - - void notifyOnFlingListeners() { - for (MapboxMap.OnFlingListener listener : onFlingListenerList) { - listener.onFling(); - } - } - - void notifyOnMoveBeginListeners(@NonNull MoveGestureDetector detector) { - for (MapboxMap.OnMoveListener listener : onMoveListenerList) { - listener.onMoveBegin(detector); - } - } - - void notifyOnMoveListeners(@NonNull MoveGestureDetector detector) { - for (MapboxMap.OnMoveListener listener : onMoveListenerList) { - listener.onMove(detector); - } - } - - void notifyOnMoveEndListeners(@NonNull MoveGestureDetector detector) { - for (MapboxMap.OnMoveListener listener : onMoveListenerList) { - listener.onMoveEnd(detector); - } - } - - void notifyOnRotateBeginListeners(@NonNull RotateGestureDetector detector) { - for (MapboxMap.OnRotateListener listener : onRotateListenerList) { - listener.onRotateBegin(detector); - } - } - - void notifyOnRotateListeners(@NonNull RotateGestureDetector detector) { - for (MapboxMap.OnRotateListener listener : onRotateListenerList) { - listener.onRotate(detector); - } - } - - void notifyOnRotateEndListeners(@NonNull RotateGestureDetector detector) { - for (MapboxMap.OnRotateListener listener : onRotateListenerList) { - listener.onRotateEnd(detector); - } - } - - void notifyOnScaleBeginListeners(@NonNull StandardScaleGestureDetector detector) { - for (MapboxMap.OnScaleListener listener : onScaleListenerList) { - listener.onScaleBegin(detector); - } - } - - void notifyOnScaleListeners(@NonNull StandardScaleGestureDetector detector) { - for (MapboxMap.OnScaleListener listener : onScaleListenerList) { - listener.onScale(detector); - } - } - - void notifyOnScaleEndListeners(@NonNull StandardScaleGestureDetector detector) { - for (MapboxMap.OnScaleListener listener : onScaleListenerList) { - listener.onScaleEnd(detector); - } - } - - void notifyOnShoveBeginListeners(@NonNull ShoveGestureDetector detector) { - for (MapboxMap.OnShoveListener listener : onShoveListenerList) { - listener.onShoveBegin(detector); - } - } - - void notifyOnShoveListeners(@NonNull ShoveGestureDetector detector) { - for (MapboxMap.OnShoveListener listener : onShoveListenerList) { - listener.onShove(detector); - } - } - - void notifyOnShoveEndListeners(@NonNull ShoveGestureDetector detector) { - for (MapboxMap.OnShoveListener listener : onShoveListenerList) { - listener.onShoveEnd(detector); - } - } - - void addOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { - onMapClickListenerList.add(onMapClickListener); - } - - void removeOnMapClickListener(MapboxMap.OnMapClickListener onMapClickListener) { - onMapClickListenerList.remove(onMapClickListener); - } - - void addOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { - onMapLongClickListenerList.add(onMapLongClickListener); - } - - void removeOnMapLongClickListener(MapboxMap.OnMapLongClickListener onMapLongClickListener) { - onMapLongClickListenerList.remove(onMapLongClickListener); - } - - void addOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { - onFlingListenerList.add(onFlingListener); - } - - void removeOnFlingListener(MapboxMap.OnFlingListener onFlingListener) { - onFlingListenerList.remove(onFlingListener); - } - - void addOnMoveListener(MapboxMap.OnMoveListener listener) { - onMoveListenerList.add(listener); - } - - void removeOnMoveListener(MapboxMap.OnMoveListener listener) { - onMoveListenerList.remove(listener); - } - - void addOnRotateListener(MapboxMap.OnRotateListener listener) { - onRotateListenerList.add(listener); - } - - void removeOnRotateListener(MapboxMap.OnRotateListener listener) { - onRotateListenerList.remove(listener); - } - - void addOnScaleListener(MapboxMap.OnScaleListener listener) { - onScaleListenerList.add(listener); - } - - void removeOnScaleListener(MapboxMap.OnScaleListener listener) { - onScaleListenerList.remove(listener); - } - - void addShoveListener(MapboxMap.OnShoveListener listener) { - onShoveListenerList.add(listener); - } - - void removeShoveListener(MapboxMap.OnShoveListener listener) { - onShoveListenerList.remove(listener); - } - - AndroidGesturesManager getGesturesManager() { - return gesturesManager; - } - - void setGesturesManager(@NonNull Context context, @NonNull AndroidGesturesManager gesturesManager, - boolean attachDefaultListeners, boolean setDefaultMutuallyExclusives) { - initializeGesturesManager(gesturesManager, setDefaultMutuallyExclusives); - initializeGestureListeners(context, attachDefaultListeners); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java deleted file mode 100644 index afb80027d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapKeyListener.java +++ /dev/null @@ -1,271 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.PointF; -import android.os.Handler; -import android.os.Looper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.ViewConfiguration; - -/** - * Manages key events on a MapView. - *

- *

    - *
  • Uses {@link Transform} to change the map state
  • - *
  • Uses {@link UiSettings} to verify validity of user restricted movement.
  • - *
- *

- */ -final class MapKeyListener { - - private final Transform transform; - private final UiSettings uiSettings; - private final MapGestureDetector mapGestureDetector; - - @Nullable - private TrackballLongPressTimeOut currentTrackballLongPressTimeOut; - - MapKeyListener(Transform transform, UiSettings uiSettings, MapGestureDetector mapGestureDetector) { - this.transform = transform; - this.uiSettings = uiSettings; - this.mapGestureDetector = mapGestureDetector; - } - - /** - * Called when the user presses a key, alse called for repeated keys held down. - * - * @param keyCode the id of the pressed key - * @param event the related key event - * @return true if the wevent is handled - */ - boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { - // If the user has held the scroll key down for a while then accelerate - // the scroll speed - double scrollDist = event.getRepeatCount() >= 5 ? 50.0 : 10.0; - - // Check which key was pressed via hardware/real key code - switch (keyCode) { - // Tell the system to track these keys for long presses on - // onKeyLongPress is fired - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - event.startTracking(); - return true; - - case KeyEvent.KEYCODE_DPAD_LEFT: - if (!uiSettings.isScrollGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move left - transform.moveBy(scrollDist, 0.0, 0 /*no animation*/); - return true; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (!uiSettings.isScrollGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move right - transform.moveBy(-scrollDist, 0.0, 0 /*no animation*/); - return true; - - case KeyEvent.KEYCODE_DPAD_UP: - if (!uiSettings.isScrollGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move up - transform.moveBy(0.0, scrollDist, 0 /*no animation*/); - return true; - - case KeyEvent.KEYCODE_DPAD_DOWN: - if (!uiSettings.isScrollGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Move down - transform.moveBy(0.0, -scrollDist, 0 /*no animation*/); - return true; - - default: - // We are not interested in this key - return false; - } - } - - /** - * Called when the user long presses a key that is being tracked. - * - * @param keyCode the id of the long pressed key - * @param event the related key event - * @return true if event is handled - */ - boolean onKeyLongPress(int keyCode, KeyEvent event) { - // Check which key was pressed via hardware/real key code - switch (keyCode) { - // Tell the system to track these keys for long presses on - // onKeyLongPress is fired - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Zoom out - PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - mapGestureDetector.zoomOutAnimated(focalPoint, true); - return true; - - default: - // We are not interested in this key - return false; - } - } - - /** - * Called when the user releases a key. - * - * @param keyCode the id of the released key - * @param event the related key event - * @return true if the event is handled - */ - boolean onKeyUp(int keyCode, KeyEvent event) { - // Check if the key action was canceled (used for virtual keyboards) - if (event.isCanceled()) { - return false; - } - - // Check which key was pressed via hardware/real key code - // Note if keyboard does not have physical key (ie primary non-shifted - // key) then it will not appear here - // Must use the key character map as physical to character is not - // fixed/guaranteed - switch (keyCode) { - case KeyEvent.KEYCODE_ENTER: - case KeyEvent.KEYCODE_DPAD_CENTER: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Zoom in - PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - mapGestureDetector.zoomInAnimated(focalPoint, true); - return true; - } - - // We are not interested in this key - return false; - } - - /** - * Called for trackball events, all motions are relative in device specific units. - * - * @param event the related motion event - * @return true if the event is handled - */ - boolean onTrackballEvent(MotionEvent event) { - // Choose the action - switch (event.getActionMasked()) { - // The trackball was rotated - case MotionEvent.ACTION_MOVE: - if (!uiSettings.isScrollGesturesEnabled()) { - return false; - } - - // Cancel any animation - transform.cancelTransitions(); - - // Scroll the map - transform.moveBy(-10.0 * event.getX(), -10.0 * event.getY(), 0 /*no animation*/); - return true; - - // Trackball was pushed in so start tracking and tell system we are - // interested - // We will then get the up action - case MotionEvent.ACTION_DOWN: - // Set up a delayed callback to check if trackball is still - // After waiting the system long press time out - if (currentTrackballLongPressTimeOut != null) { - currentTrackballLongPressTimeOut.cancel(); - currentTrackballLongPressTimeOut = null; - } - currentTrackballLongPressTimeOut = new TrackballLongPressTimeOut(); - new Handler(Looper.getMainLooper()).postDelayed(currentTrackballLongPressTimeOut, - ViewConfiguration.getLongPressTimeout()); - return true; - - // Trackball was released - case MotionEvent.ACTION_UP: - if (!uiSettings.isZoomGesturesEnabled()) { - return false; - } - - // Only handle if we have not already long pressed - if (currentTrackballLongPressTimeOut != null) { - // Zoom in - PointF focalPoint = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - mapGestureDetector.zoomInAnimated(focalPoint, true); - } - return true; - - // Trackball was cancelled - case MotionEvent.ACTION_CANCEL: - if (currentTrackballLongPressTimeOut != null) { - currentTrackballLongPressTimeOut.cancel(); - currentTrackballLongPressTimeOut = null; - } - return true; - - default: - // We are not interested in this event - return false; - } - } - - /** - * This class implements the trackball long press time out callback - */ - private class TrackballLongPressTimeOut implements Runnable { - - // Track if we have been cancelled - private boolean cancelled; - - TrackballLongPressTimeOut() { - cancelled = false; - } - - // Cancel the timeout - public void cancel() { - cancelled = true; - } - - // Called when long press time out expires - @Override - public void run() { - // Check if the trackball is still pressed - if (!cancelled) { - // Zoom out - PointF pointF = new PointF(uiSettings.getWidth() / 2, uiSettings.getHeight() / 2); - mapGestureDetector.zoomOutAnimated(pointF, true); - - // Ensure the up action is not run - currentTrackballLongPressTimeOut = null; - } - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java deleted file mode 100644 index 4521d2ae6..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java +++ /dev/null @@ -1,1350 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.graphics.drawable.ColorDrawable; -import android.os.Bundle; -import android.support.annotation.CallSuper; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.UiThread; -import android.support.v4.util.LongSparseArray; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.TextureView; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.ImageView; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.Mapbox; -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.mapboxsdk.location.LocationComponent; -import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; -import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.GLSurfaceViewMapRenderer; -import com.mapbox.mapboxsdk.maps.renderer.glsurfaceview.MapboxGLSurfaceView; -import com.mapbox.mapboxsdk.maps.renderer.textureview.TextureViewMapRenderer; -import com.mapbox.mapboxsdk.maps.widgets.CompassView; -import com.mapbox.mapboxsdk.net.ConnectivityReceiver; -import com.mapbox.mapboxsdk.storage.FileSource; -import com.mapbox.mapboxsdk.utils.BitmapUtils; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.microedition.khronos.egl.EGLConfig; -import javax.microedition.khronos.opengles.GL10; - -import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_MAP_NORTH_ANIMATION; -import static com.mapbox.mapboxsdk.maps.widgets.CompassView.TIME_WAIT_IDLE; - -/** - *

- * A {@code MapView} provides an embeddable map interface. - * You use this class to display map information and to manipulate the map contents from your application. - * You can center the map on a given coordinate, specify the size of the area you want to display, - * and style the features of the map to fit your application's use case. - *

- *

- * Use of {@code MapView} requires a Mapbox API access token. - * Obtain an access token on the Mapbox account page. - *

- * Warning: Please note that you are responsible for getting permission to use the map data, - * and for ensuring your use adheres to the relevant terms of use. - */ -public class MapView extends FrameLayout implements NativeMapView.ViewCallback { - - private final MapChangeReceiver mapChangeReceiver = new MapChangeReceiver(); - private final MapCallback mapCallback = new MapCallback(); - private final InitialRenderCallback initialRenderCallback = new InitialRenderCallback(); - - @Nullable - private NativeMap nativeMapView; - @Nullable - private MapboxMap mapboxMap; - private AttributionClickListener attributionClickListener; - private MapboxMapOptions mapboxMapOptions; - private MapRenderer mapRenderer; - private boolean destroyed; - - private CompassView compassView; - private PointF focalPoint; - private ImageView attrView; - private ImageView logoView; - - @Nullable - private MapGestureDetector mapGestureDetector; - @Nullable - private MapKeyListener mapKeyListener; - @Nullable - private Bundle savedInstanceState; - private boolean isStarted; - - @UiThread - public MapView(@NonNull Context context) { - super(context); - initialize(context, MapboxMapOptions.createFromAttributes(context)); - } - - @UiThread - public MapView(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); - } - - @UiThread - public MapView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - initialize(context, MapboxMapOptions.createFromAttributes(context, attrs)); - } - - @UiThread - public MapView(@NonNull Context context, @Nullable MapboxMapOptions options) { - super(context); - initialize(context, options == null ? MapboxMapOptions.createFromAttributes(context) : options); - } - - @CallSuper - @UiThread - protected void initialize(@NonNull final Context context, @NonNull final MapboxMapOptions options) { - if (isInEditMode()) { - // in IDE layout editor, just return - return; - } - - if (!Mapbox.hasInstance()) { - throw new MapboxConfigurationException(); - } - - // hide surface until map is fully loaded #10990 - setForeground(new ColorDrawable(options.getForegroundLoadColor())); - - mapboxMapOptions = options; - - // inflate view - View view = LayoutInflater.from(context).inflate(R.layout.mapbox_mapview_internal, this); - compassView = view.findViewById(R.id.compassView); - attrView = view.findViewById(R.id.attributionView); - attrView.setImageDrawable(BitmapUtils.getDrawableFromRes(getContext(), R.drawable.mapbox_info_bg_selector)); - logoView = view.findViewById(R.id.logoView); - logoView.setImageDrawable(BitmapUtils.getDrawableFromRes(getContext(), R.drawable.mapbox_logo_icon)); - - // add accessibility support - setContentDescription(context.getString(R.string.mapbox_mapActionDescription)); - setWillNotDraw(false); - initialiseDrawingSurface(options); - } - - private void initialiseMap() { - Context context = getContext(); - - // callback for focal point invalidation - final FocalPointInvalidator focalInvalidator = new FocalPointInvalidator(); - focalInvalidator.addListener(createFocalPointChangeListener()); - - // callback for registering touch listeners - GesturesManagerInteractionListener registerTouchListener = new GesturesManagerInteractionListener(); - - // callback for camera change events - final CameraChangeDispatcher cameraDispatcher = new CameraChangeDispatcher(); - - // setup components for MapboxMap creation - Projection proj = new Projection(nativeMapView, this); - UiSettings uiSettings = new UiSettings(proj, focalInvalidator, compassView, attrView, logoView, getPixelRatio()); - LongSparseArray annotationsArray = new LongSparseArray<>(); - IconManager iconManager = new IconManager(nativeMapView); - Annotations annotations = new AnnotationContainer(nativeMapView, annotationsArray); - Markers markers = new MarkerContainer(nativeMapView, annotationsArray, iconManager); - Polygons polygons = new PolygonContainer(nativeMapView, annotationsArray); - Polylines polylines = new PolylineContainer(nativeMapView, annotationsArray); - ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(nativeMapView, annotationsArray); - AnnotationManager annotationManager = new AnnotationManager(this, annotationsArray, iconManager, - annotations, markers, polygons, polylines, shapeAnnotations); - Transform transform = new Transform(this, nativeMapView, cameraDispatcher); - - // MapboxMap - List developerAnimationListeners = new ArrayList<>(); - mapboxMap = new MapboxMap(nativeMapView, transform, uiSettings, proj, registerTouchListener, cameraDispatcher, - developerAnimationListeners); - mapboxMap.injectAnnotationManager(annotationManager); - - // user input - mapGestureDetector = new MapGestureDetector(context, transform, proj, uiSettings, - annotationManager, cameraDispatcher); - mapKeyListener = new MapKeyListener(transform, uiSettings, mapGestureDetector); - - // compass - compassView.injectCompassAnimationListener(createCompassAnimationListener(cameraDispatcher)); - compassView.setOnClickListener(createCompassClickListener(cameraDispatcher)); - - // LocationComponent - mapboxMap.injectLocationComponent(new LocationComponent(mapboxMap, transform, developerAnimationListeners)); - - // inject widgets with MapboxMap - attrView.setOnClickListener(attributionClickListener = new AttributionClickListener(context, mapboxMap)); - - // Ensure this view is interactable - setClickable(true); - setLongClickable(true); - setFocusable(true); - setFocusableInTouchMode(true); - requestDisallowInterceptTouchEvent(true); - - // notify Map object about current connectivity state - nativeMapView.setReachability(Mapbox.isConnected()); - - // initialise MapboxMap - if (savedInstanceState == null) { - mapboxMap.initialise(context, mapboxMapOptions); - } else { - mapboxMap.onRestoreInstanceState(savedInstanceState); - } - - mapCallback.initialised(); - } - - private FocalPointChangeListener createFocalPointChangeListener() { - return new FocalPointChangeListener() { - @Override - public void onFocalPointChanged(PointF pointF) { - focalPoint = pointF; - } - }; - } - - private MapboxMap.OnCompassAnimationListener createCompassAnimationListener(@NonNull final CameraChangeDispatcher - cameraChangeDispatcher) { - return new MapboxMap.OnCompassAnimationListener() { - @Override - public void onCompassAnimation() { - cameraChangeDispatcher.onCameraMove(); - } - - @Override - public void onCompassAnimationFinished() { - compassView.isAnimating(false); - cameraChangeDispatcher.onCameraIdle(); - } - }; - } - - private OnClickListener createCompassClickListener(@NonNull final CameraChangeDispatcher cameraChangeDispatcher) { - return new OnClickListener() { - @Override - public void onClick(View v) { - if (mapboxMap != null && compassView != null) { - if (focalPoint != null) { - mapboxMap.setFocalBearing(0, focalPoint.x, focalPoint.y, TIME_MAP_NORTH_ANIMATION); - } else { - mapboxMap.setFocalBearing(0, mapboxMap.getWidth() / 2, mapboxMap.getHeight() / 2, TIME_MAP_NORTH_ANIMATION); - } - cameraChangeDispatcher.onCameraMoveStarted(MapboxMap.OnCameraMoveStartedListener.REASON_API_ANIMATION); - compassView.isAnimating(true); - compassView.postDelayed(compassView, TIME_WAIT_IDLE + TIME_MAP_NORTH_ANIMATION); - } - } - }; - } - - // - // Lifecycle events - // - - /** - *

- * You must call this method from the parent's Activity#onCreate(Bundle)} or - * Fragment#onViewCreated(View, Bundle). - *

- * You must set a valid access token with {@link com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String)} - * before you call this method or an exception will be thrown. - * - * @param savedInstanceState Pass in the parent's savedInstanceState. - * @see com.mapbox.mapboxsdk.Mapbox#getInstance(Context, String) - */ - @UiThread - public void onCreate(@Nullable Bundle savedInstanceState) { - if (savedInstanceState == null) { - TelemetryDefinition telemetry = Mapbox.getTelemetry(); - if (telemetry != null) { - telemetry.onAppUserTurnstileEvent(); - } - } else if (savedInstanceState.getBoolean(MapboxConstants.STATE_HAS_SAVED_STATE)) { - this.savedInstanceState = savedInstanceState; - } - } - - private void initialiseDrawingSurface(MapboxMapOptions options) { - String localFontFamily = options.getLocalIdeographFontFamily(); - if (options.getTextureMode()) { - TextureView textureView = new TextureView(getContext()); - boolean translucentSurface = options.getTranslucentTextureSurface(); - mapRenderer = new TextureViewMapRenderer(getContext(), - textureView, localFontFamily, translucentSurface) { - @Override - protected void onSurfaceCreated(GL10 gl, EGLConfig config) { - MapView.this.onSurfaceCreated(); - super.onSurfaceCreated(gl, config); - } - }; - - addView(textureView, 0); - } else { - MapboxGLSurfaceView glSurfaceView = new MapboxGLSurfaceView(getContext()); - glSurfaceView.setZOrderMediaOverlay(mapboxMapOptions.getRenderSurfaceOnTop()); - mapRenderer = new GLSurfaceViewMapRenderer(getContext(), glSurfaceView, localFontFamily) { - @Override - public void onSurfaceCreated(GL10 gl, EGLConfig config) { - MapView.this.onSurfaceCreated(); - super.onSurfaceCreated(gl, config); - } - }; - - addView(glSurfaceView, 0); - } - - boolean crossSourceCollisions = mapboxMapOptions.getCrossSourceCollisions(); - nativeMapView = new NativeMapView( - getContext(), getPixelRatio(), crossSourceCollisions, this, mapChangeReceiver, mapRenderer - ); - } - - private void onSurfaceCreated() { - post(new Runnable() { - @Override - public void run() { - // Initialise only when not destroyed and only once - if (!destroyed && mapboxMap == null) { - MapView.this.initialiseMap(); - mapboxMap.onStart(); - } - } - }); - } - - /** - * You must call this method from the parent's Activity#onSaveInstanceState(Bundle) - * or Fragment#onSaveInstanceState(Bundle). - * - * @param outState Pass in the parent's outState. - */ - @UiThread - public void onSaveInstanceState(@NonNull Bundle outState) { - if (mapboxMap != null) { - outState.putBoolean(MapboxConstants.STATE_HAS_SAVED_STATE, true); - mapboxMap.onSaveInstanceState(outState); - } - } - - /** - * You must call this method from the parent's Activity#onStart() or Fragment#onStart() - */ - @UiThread - public void onStart() { - if (!isStarted) { - ConnectivityReceiver.instance(getContext()).activate(); - FileSource.getInstance(getContext()).activate(); - isStarted = true; - } - if (mapboxMap != null) { - mapboxMap.onStart(); - } - - if (mapRenderer != null) { - mapRenderer.onStart(); - } - } - - /** - * You must call this method from the parent's Activity#onResume() or Fragment#onResume(). - */ - @UiThread - public void onResume() { - if (mapRenderer != null) { - mapRenderer.onResume(); - } - } - - /** - * You must call this method from the parent's Activity#onPause() or Fragment#onPause(). - */ - @UiThread - public void onPause() { - if (mapRenderer != null) { - mapRenderer.onPause(); - } - } - - /** - * You must call this method from the parent's Activity#onStop() or Fragment#onStop(). - */ - @UiThread - public void onStop() { - if (attributionClickListener != null) { - attributionClickListener.onStop(); - } - - if (mapboxMap != null) { - // map was destroyed before it was started - mapGestureDetector.cancelAnimators(); - mapboxMap.onStop(); - } - - if (mapRenderer != null) { - mapRenderer.onStop(); - } - - if (isStarted) { - ConnectivityReceiver.instance(getContext()).deactivate(); - FileSource.getInstance(getContext()).deactivate(); - isStarted = false; - } - } - - /** - * You must call this method from the parent's Activity#onDestroy() or Fragment#onDestroyView(). - */ - @UiThread - public void onDestroy() { - destroyed = true; - mapChangeReceiver.clear(); - mapCallback.onDestroy(); - initialRenderCallback.onDestroy(); - - if (compassView != null) { - // avoid leaking context through animator #13742 - compassView.resetAnimation(); - } - - if (mapboxMap != null) { - mapboxMap.onDestroy(); - } - - if (nativeMapView != null) { - // null when destroying an activity programmatically mapbox-navigation-android/issues/503 - nativeMapView.destroy(); - nativeMapView = null; - } - - if (mapRenderer != null) { - mapRenderer.onDestroy(); - } - } - - /** - * The maximum frame rate at which the map view is rendered, - * but it can't excess the ability of device hardware. - * - * @param maximumFps Can be set to arbitrary integer values. - */ - public void setMaximumFps(int maximumFps) { - if (mapRenderer != null) { - mapRenderer.setMaximumFps(maximumFps); - } else { - throw new IllegalStateException("Calling MapView#setMaximumFps before mapRenderer is created."); - } - } - - /** - * Returns if the map has been destroyed. - *

- * This method can be used to determine if the result of an asynchronous operation should be set. - *

- * - * @return true, if the map has been destroyed - */ - @UiThread - public boolean isDestroyed() { - return destroyed; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - if (!isGestureDetectorInitialized()) { - return super.onTouchEvent(event); - } - - return mapGestureDetector.onTouchEvent(event) || super.onTouchEvent(event); - } - - @Override - public boolean onKeyDown(int keyCode, @NonNull KeyEvent event) { - return mapKeyListener.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); - } - - @Override - public boolean onKeyLongPress(int keyCode, KeyEvent event) { - return mapKeyListener.onKeyLongPress(keyCode, event) || super.onKeyLongPress(keyCode, event); - } - - @Override - public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) { - return mapKeyListener.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); - } - - @Override - public boolean onTrackballEvent(@NonNull MotionEvent event) { - return mapKeyListener.onTrackballEvent(event) || super.onTrackballEvent(event); - } - - @Override - public boolean onGenericMotionEvent(@NonNull MotionEvent event) { - if (!isGestureDetectorInitialized()) { - return super.onGenericMotionEvent(event); - } - return mapGestureDetector.onGenericMotionEvent(event) || super.onGenericMotionEvent(event); - } - - /** - * You must call this method from the parent's Activity#onLowMemory() or Fragment#onLowMemory(). - */ - @UiThread - public void onLowMemory() { - if (nativeMapView != null && mapboxMap != null && !destroyed) { - nativeMapView.onLowMemory(); - } - } - - // - // Rendering - // - - @Override - protected void onSizeChanged(int width, int height, int oldw, int oldh) { - if (!isInEditMode() && nativeMapView != null) { - // null-checking the nativeMapView, see #13277 - nativeMapView.resizeView(width, height); - } - } - - private float getPixelRatio() { - // check is user defined his own pixel ratio value - float pixelRatio = mapboxMapOptions.getPixelRatio(); - if (pixelRatio == 0) { - // if not, get the one defined by the system - pixelRatio = getResources().getDisplayMetrics().density; - } - return pixelRatio; - } - - // - // ViewCallback - // - - @Nullable - @Override - public Bitmap getViewContent() { - return BitmapUtils.createBitmapFromView(this); - } - - // - // Map events - // - - /** - * Set a callback that's invoked when the camera region will change. - * - * @param listener The callback that's invoked when the camera region will change - */ - public void addOnCameraWillChangeListener(@NonNull OnCameraWillChangeListener listener) { - mapChangeReceiver.addOnCameraWillChangeListener(listener); - } - - /** - * Remove a callback that's invoked when the camera region will change. - * - * @param listener The callback that's invoked when the camera region will change - */ - public void removeOnCameraWillChangeListener(@NonNull OnCameraWillChangeListener listener) { - mapChangeReceiver.removeOnCameraWillChangeListener(listener); - } - - /** - * Set a callback that's invoked when the camera is changing. - * - * @param listener The callback that's invoked when the camera is changing - */ - public void addOnCameraIsChangingListener(@NonNull OnCameraIsChangingListener listener) { - mapChangeReceiver.addOnCameraIsChangingListener(listener); - } - - /** - * Remove a callback that's invoked when the camera is changing. - * - * @param listener The callback that's invoked when the camera is changing - */ - public void removeOnCameraIsChangingListener(@NonNull OnCameraIsChangingListener listener) { - mapChangeReceiver.removeOnCameraIsChangingListener(listener); - } - - /** - * Set a callback that's invoked when the camera region did change. - * - * @param listener The callback that's invoked when the camera region did change - */ - public void addOnCameraDidChangeListener(@NonNull OnCameraDidChangeListener listener) { - mapChangeReceiver.addOnCameraDidChangeListener(listener); - } - - /** - * Set a callback that's invoked when the camera region did change. - * - * @param listener The callback that's invoked when the camera region did change - */ - public void removeOnCameraDidChangeListener(@NonNull OnCameraDidChangeListener listener) { - mapChangeReceiver.removeOnCameraDidChangeListener(listener); - } - - /** - * Set a callback that's invoked when the map will start loading. - * - * @param listener The callback that's invoked when the map will start loading - */ - public void addOnWillStartLoadingMapListener(@NonNull OnWillStartLoadingMapListener listener) { - mapChangeReceiver.addOnWillStartLoadingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map will start loading. - * - * @param listener The callback that's invoked when the map will start loading - */ - public void removeOnWillStartLoadingMapListener(@NonNull OnWillStartLoadingMapListener listener) { - mapChangeReceiver.removeOnWillStartLoadingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map has finished loading. - * - * @param listener The callback that's invoked when the map has finished loading - */ - public void addOnDidFinishLoadingMapListener(@NonNull OnDidFinishLoadingMapListener listener) { - mapChangeReceiver.addOnDidFinishLoadingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map has finished loading. - * - * @param listener The callback that's invoked when the map has finished loading - */ - public void removeOnDidFinishLoadingMapListener(@NonNull OnDidFinishLoadingMapListener listener) { - mapChangeReceiver.removeOnDidFinishLoadingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map failed to load. - * - * @param listener The callback that's invoked when the map failed to load - */ - public void addOnDidFailLoadingMapListener(@NonNull OnDidFailLoadingMapListener listener) { - mapChangeReceiver.addOnDidFailLoadingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map failed to load. - * - * @param listener The callback that's invoked when the map failed to load - */ - public void removeOnDidFailLoadingMapListener(@NonNull OnDidFailLoadingMapListener listener) { - mapChangeReceiver.removeOnDidFailLoadingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map will start rendering a frame. - * - * @param listener The callback that's invoked when the camera will start rendering a frame - */ - public void addOnWillStartRenderingFrameListener(@NonNull OnWillStartRenderingFrameListener listener) { - mapChangeReceiver.addOnWillStartRenderingFrameListener(listener); - } - - /** - * Set a callback that's invoked when the map will start rendering a frame. - * - * @param listener The callback that's invoked when the camera will start rendering a frame - */ - public void removeOnWillStartRenderingFrameListener(@NonNull OnWillStartRenderingFrameListener listener) { - mapChangeReceiver.removeOnWillStartRenderingFrameListener(listener); - } - - /** - * Set a callback that's invoked when the map has finished rendering a frame. - * - * @param listener The callback that's invoked when the map has finished rendering a frame - */ - public void addOnDidFinishRenderingFrameListener(@NonNull OnDidFinishRenderingFrameListener listener) { - mapChangeReceiver.addOnDidFinishRenderingFrameListener(listener); - } - - /** - * Set a callback that's invoked when the map has finished rendering a frame. - * - * @param listener The callback that's invoked when the map has finished rendering a frame - */ - public void removeOnDidFinishRenderingFrameListener(@NonNull OnDidFinishRenderingFrameListener listener) { - mapChangeReceiver.removeOnDidFinishRenderingFrameListener(listener); - } - - /** - * Set a callback that's invoked when the map will start rendering. - * - * @param listener The callback that's invoked when the map will start rendering - */ - public void addOnWillStartRenderingMapListener(@NonNull OnWillStartRenderingMapListener listener) { - mapChangeReceiver.addOnWillStartRenderingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map will start rendering. - * - * @param listener The callback that's invoked when the map will start rendering - */ - public void removeOnWillStartRenderingMapListener(@NonNull OnWillStartRenderingMapListener listener) { - mapChangeReceiver.removeOnWillStartRenderingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map has finished rendering. - * - * @param listener The callback that's invoked when the map has finished rendering - */ - public void addOnDidFinishRenderingMapListener(@NonNull OnDidFinishRenderingMapListener listener) { - mapChangeReceiver.addOnDidFinishRenderingMapListener(listener); - } - - /** - * Remove a callback that's invoked when the map has finished rendering. - * - * @param listener The callback that's invoked when the map has has finished rendering. - */ - public void removeOnDidFinishRenderingMapListener(OnDidFinishRenderingMapListener listener) { - mapChangeReceiver.removeOnDidFinishRenderingMapListener(listener); - } - - /** - * Set a callback that's invoked when the map has entered the idle state. - * - * @param listener The callback that's invoked when the map has entered the idle state. - */ - public void addOnDidBecomeIdleListener(@NonNull OnDidBecomeIdleListener listener) { - mapChangeReceiver.addOnDidBecomeIdleListener(listener); - } - - /** - * Remove a callback that's invoked when the map has entered the idle state. - * - * @param listener The callback that's invoked when the map has entered the idle state. - */ - public void removeOnDidBecomeIdleListener(@NonNull OnDidBecomeIdleListener listener) { - mapChangeReceiver.removeOnDidBecomeIdleListener(listener); - } - - /** - * Set a callback that's invoked when the style has finished loading. - * - * @param listener The callback that's invoked when the style has finished loading - */ - public void addOnDidFinishLoadingStyleListener(@NonNull OnDidFinishLoadingStyleListener listener) { - mapChangeReceiver.addOnDidFinishLoadingStyleListener(listener); - } - - /** - * Set a callback that's invoked when the style has finished loading. - * - * @param listener The callback that's invoked when the style has finished loading - */ - public void removeOnDidFinishLoadingStyleListener(@NonNull OnDidFinishLoadingStyleListener listener) { - mapChangeReceiver.removeOnDidFinishLoadingStyleListener(listener); - } - - /** - * Set a callback that's invoked when a map source has changed. - * - * @param listener The callback that's invoked when the source has changed - */ - public void addOnSourceChangedListener(@NonNull OnSourceChangedListener listener) { - mapChangeReceiver.addOnSourceChangedListener(listener); - } - - /** - * Set a callback that's invoked when a map source has changed. - * - * @param listener The callback that's invoked when the source has changed - */ - public void removeOnSourceChangedListener(@NonNull OnSourceChangedListener listener) { - mapChangeReceiver.removeOnSourceChangedListener(listener); - } - - /** - * Set a callback that's invoked when the id of an icon is missing. - * - * @param listener The callback that's invoked when the id of an icon is missing - */ - public void addOnStyleImageMissingListener(@NonNull OnStyleImageMissingListener listener) { - mapChangeReceiver.addOnStyleImageMissingListener(listener); - } - - /** - * Set a callback that's invoked when a map source has changed. - * - * @param listener The callback that's invoked when the source has changed - */ - public void removeOnStyleImageMissingListener(@NonNull OnStyleImageMissingListener listener) { - mapChangeReceiver.removeOnStyleImageMissingListener(listener); - } - - /** - * Set a callback that's invoked when map needs to release unused image resources. - *

- * A callback will be called only for unused images that were provided by the client via - * {@link OnStyleImageMissingListener#onStyleImageMissing(String)} listener interface. - *

- *

- * By default, platform will remove unused images from the style. By adding listener, default - * behavior can be overridden and client can control whether to release unused resources. - *

- * - * @param listener The callback that's invoked when map needs to release unused image resources - */ - public void addOnCanRemoveUnusedStyleImageListener(@NonNull OnCanRemoveUnusedStyleImageListener listener) { - mapChangeReceiver.addOnCanRemoveUnusedStyleImageListener(listener); - } - - /** - * Removes a callback that's invoked when map needs to release unused image resources. - *

- * When all listeners are removed, platform will fallback to default behavior, which is to remove - * unused images from the style. - *

- * - * @param listener The callback that's invoked when map needs to release unused image resources - */ - public void removeOnCanRemoveUnusedStyleImageListener(@NonNull OnCanRemoveUnusedStyleImageListener listener) { - mapChangeReceiver.removeOnCanRemoveUnusedStyleImageListener(listener); - } - - /** - * Interface definition for a callback to be invoked when the camera will change. - *

- * {@link MapView#addOnCameraWillChangeListener(OnCameraWillChangeListener)} - *

- */ - public interface OnCameraWillChangeListener { - - /** - * Called when the camera region will change. - */ - void onCameraWillChange(boolean animated); - } - - /** - * Interface definition for a callback to be invoked when the camera is changing. - *

- * {@link MapView#addOnCameraIsChangingListener(OnCameraIsChangingListener)} - *

- */ - public interface OnCameraIsChangingListener { - /** - * Called when the camera is changing. - */ - void onCameraIsChanging(); - } - - /** - * Interface definition for a callback to be invoked when the map region did change. - *

- * {@link MapView#addOnCameraDidChangeListener(OnCameraDidChangeListener)} - *

- */ - public interface OnCameraDidChangeListener { - /** - * Called when the camera did change. - */ - void onCameraDidChange(boolean animated); - } - - /** - * Interface definition for a callback to be invoked when the map will start loading. - *

- * {@link MapView#addOnWillStartLoadingMapListener(OnWillStartLoadingMapListener)} - *

- */ - public interface OnWillStartLoadingMapListener { - /** - * Called when the map will start loading. - */ - void onWillStartLoadingMap(); - } - - /** - * Interface definition for a callback to be invoked when the map finished loading. - *

- * {@link MapView#addOnDidFinishLoadingMapListener(OnDidFinishLoadingMapListener)} - *

- */ - public interface OnDidFinishLoadingMapListener { - /** - * Called when the map has finished loading. - */ - void onDidFinishLoadingMap(); - } - - /** - * Interface definition for a callback to be invoked when the map is changing. - *

- * {@link MapView#addOnDidFailLoadingMapListener(OnDidFailLoadingMapListener)} - *

- */ - public interface OnDidFailLoadingMapListener { - /** - * Called when the map failed to load. - * - * @param errorMessage The reason why the map failed to load - */ - void onDidFailLoadingMap(String errorMessage); - } - - /** - * Interface definition for a callback to be invoked when the map will start rendering a frame. - *

- * {@link MapView#addOnWillStartRenderingFrameListener(OnWillStartRenderingFrameListener)} - *

- */ - public interface OnWillStartRenderingFrameListener { - /** - * Called when the map will start rendering a frame. - */ - void onWillStartRenderingFrame(); - } - - /** - * Interface definition for a callback to be invoked when the map finished rendering a frame. - *

- * {@link MapView#addOnDidFinishRenderingFrameListener(OnDidFinishRenderingFrameListener)} - *

- */ - public interface OnDidFinishRenderingFrameListener { - /** - * Called when the map has finished rendering a frame - * - * @param fully true if all frames have been rendered, false if partially rendered - */ - void onDidFinishRenderingFrame(boolean fully); - } - - /** - * Interface definition for a callback to be invoked when the map will start rendering the map. - *

- * {@link MapView#addOnDidFailLoadingMapListener(OnDidFailLoadingMapListener)} - *

- */ - public interface OnWillStartRenderingMapListener { - /** - * Called when the map will start rendering. - */ - void onWillStartRenderingMap(); - } - - /** - * Interface definition for a callback to be invoked when the map is changing. - *

- * {@link MapView#addOnDidFinishRenderingMapListener(OnDidFinishRenderingMapListener)} - *

- */ - public interface OnDidFinishRenderingMapListener { - /** - * Called when the map has finished rendering. - * - * @param fully true if map is fully rendered, false if not fully rendered - */ - void onDidFinishRenderingMap(boolean fully); - } - - /** - * Interface definition for a callback to be invoked when the map has entered the idle state. - *

- * Calling {@link MapboxMap#snapshot(MapboxMap.SnapshotReadyCallback)} from this callback - * will result in recursive execution. Use {@link OnDidFinishRenderingFrameListener} instead. - *

- *

- * {@link MapView#addOnDidBecomeIdleListener(OnDidBecomeIdleListener)} - *

- */ - public interface OnDidBecomeIdleListener { - /** - * Called when the map has entered the idle state. - */ - void onDidBecomeIdle(); - } - - /** - * Interface definition for a callback to be invoked when the map has loaded the style. - *

- * {@link MapView#addOnDidFailLoadingMapListener(OnDidFailLoadingMapListener)} - *

- */ - public interface OnDidFinishLoadingStyleListener { - /** - * Called when a style has finished loading. - */ - void onDidFinishLoadingStyle(); - } - - /** - * Interface definition for a callback to be invoked when a map source has changed. - *

- * {@link MapView#addOnDidFailLoadingMapListener(OnDidFailLoadingMapListener)} - *

- */ - public interface OnSourceChangedListener { - /** - * Called when a map source has changed. - * - * @param id the id of the source that has changed - */ - void onSourceChangedListener(String id); - } - - /** - * Interface definition for a callback to be invoked with the id of a missing icon. The icon should be added - * synchronously with {@link Style#addImage(String, Bitmap)} to be rendered on the current zoom level. When loading - * icons asynchronously, you can load a placeholder image and replace it when you icon has loaded. - *

- * {@link MapView#addOnStyleImageMissingListener(OnStyleImageMissingListener)} - *

- */ - public interface OnStyleImageMissingListener { - /** - * Called when the map is missing an icon.The icon should be added synchronously with - * {@link Style#addImage(String, Bitmap)} to be rendered on the current zoom level. When loading icons - * asynchronously, you can load a placeholder image and replace it when you icon has loaded. - * - * @param id the id of the icon that is missing - */ - void onStyleImageMissing(@NonNull String id); - } - - /** - * Interface definition for a callback to be invoked with an unused image identifier. - *

- * {@link MapView#addOnCanRemoveUnusedStyleImageListener(OnCanRemoveUnusedStyleImageListener)} - *

- */ - public interface OnCanRemoveUnusedStyleImageListener { - /** - * Called when the map needs to release unused image resources. - * - * @param id of an image that is not used by the map and can be removed from the style. - * @return true if image can be removed, false otherwise. - */ - boolean onCanRemoveUnusedStyleImage(@NonNull String id); - } - - /** - * Sets a callback object which will be triggered when the {@link MapboxMap} instance is ready to be used. - * - * @param callback The callback object that will be triggered when the map is ready to be used. - */ - @UiThread - public void getMapAsync(final @NonNull OnMapReadyCallback callback) { - if (mapboxMap == null) { - // Add callback to the list only if the style hasn't loaded, or the drawing surface isn't ready - mapCallback.addOnMapReadyCallback(callback); - } else { - callback.onMapReady(mapboxMap); - } - } - - private boolean isGestureDetectorInitialized() { - return mapGestureDetector != null; - } - - @Nullable - MapboxMap getMapboxMap() { - return mapboxMap; - } - - void setMapboxMap(MapboxMap mapboxMap) { - this.mapboxMap = mapboxMap; - } - - private class FocalPointInvalidator implements FocalPointChangeListener { - - private final List focalPointChangeListeners = new ArrayList<>(); - - void addListener(FocalPointChangeListener focalPointChangeListener) { - focalPointChangeListeners.add(focalPointChangeListener); - } - - @Override - public void onFocalPointChanged(PointF pointF) { - mapGestureDetector.setFocalPoint(pointF); - for (FocalPointChangeListener focalPointChangeListener : focalPointChangeListeners) { - focalPointChangeListener.onFocalPointChanged(pointF); - } - } - } - - /** - * The initial render callback waits for rendering to happen before making the map visible for end-users. - * We wait for the second DID_FINISH_RENDERING_FRAME map change event as the first will still show a black surface. - */ - private class InitialRenderCallback implements OnDidFinishRenderingFrameListener { - - private int renderCount; - - InitialRenderCallback() { - addOnDidFinishRenderingFrameListener(this); - } - - @Override - public void onDidFinishRenderingFrame(boolean fully) { - if (mapboxMap != null && mapboxMap.getStyle() != null && mapboxMap.getStyle().isFullyLoaded()) { - renderCount++; - if (renderCount == 3) { - MapView.this.setForeground(null); - removeOnDidFinishRenderingFrameListener(this); - } - } - } - - private void onDestroy() { - removeOnDidFinishRenderingFrameListener(this); - } - } - - private class GesturesManagerInteractionListener implements MapboxMap.OnGesturesManagerInteractionListener { - - @Override - public void onAddMapClickListener(MapboxMap.OnMapClickListener listener) { - mapGestureDetector.addOnMapClickListener(listener); - } - - @Override - public void onRemoveMapClickListener(MapboxMap.OnMapClickListener listener) { - mapGestureDetector.removeOnMapClickListener(listener); - } - - @Override - public void onAddMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { - mapGestureDetector.addOnMapLongClickListener(listener); - } - - @Override - public void onRemoveMapLongClickListener(MapboxMap.OnMapLongClickListener listener) { - mapGestureDetector.removeOnMapLongClickListener(listener); - } - - @Override - public void onAddFlingListener(MapboxMap.OnFlingListener listener) { - mapGestureDetector.addOnFlingListener(listener); - } - - @Override - public void onRemoveFlingListener(MapboxMap.OnFlingListener listener) { - mapGestureDetector.removeOnFlingListener(listener); - } - - @Override - public void onAddMoveListener(MapboxMap.OnMoveListener listener) { - mapGestureDetector.addOnMoveListener(listener); - } - - @Override - public void onRemoveMoveListener(MapboxMap.OnMoveListener listener) { - mapGestureDetector.removeOnMoveListener(listener); - } - - @Override - public void onAddRotateListener(MapboxMap.OnRotateListener listener) { - mapGestureDetector.addOnRotateListener(listener); - } - - @Override - public void onRemoveRotateListener(MapboxMap.OnRotateListener listener) { - mapGestureDetector.removeOnRotateListener(listener); - } - - @Override - public void onAddScaleListener(MapboxMap.OnScaleListener listener) { - mapGestureDetector.addOnScaleListener(listener); - } - - @Override - public void onRemoveScaleListener(MapboxMap.OnScaleListener listener) { - mapGestureDetector.removeOnScaleListener(listener); - } - - @Override - public void onAddShoveListener(MapboxMap.OnShoveListener listener) { - mapGestureDetector.addShoveListener(listener); - } - - @Override - public void onRemoveShoveListener(MapboxMap.OnShoveListener listener) { - mapGestureDetector.removeShoveListener(listener); - } - - @Override - public AndroidGesturesManager getGesturesManager() { - return mapGestureDetector.getGesturesManager(); - } - - @Override - public void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners, - boolean setDefaultMutuallyExclusives) { - mapGestureDetector.setGesturesManager( - getContext(), gesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives); - } - - @Override - public void cancelAllVelocityAnimations() { - mapGestureDetector.cancelAnimators(); - } - } - - private class MapCallback implements OnDidFinishLoadingStyleListener, - OnDidFinishRenderingFrameListener, OnDidFinishLoadingMapListener, - OnCameraIsChangingListener, OnCameraDidChangeListener, OnDidFailLoadingMapListener { - - private final List onMapReadyCallbackList = new ArrayList<>(); - - MapCallback() { - addOnDidFinishLoadingStyleListener(this); - addOnDidFinishRenderingFrameListener(this); - addOnDidFinishLoadingMapListener(this); - addOnCameraIsChangingListener(this); - addOnCameraDidChangeListener(this); - addOnDidFailLoadingMapListener(this); - } - - void initialised() { - mapboxMap.onPreMapReady(); - onMapReady(); - mapboxMap.onPostMapReady(); - } - - /** - * Notify listeners, clear when done - */ - private void onMapReady() { - if (onMapReadyCallbackList.size() > 0) { - Iterator iterator = onMapReadyCallbackList.iterator(); - while (iterator.hasNext()) { - OnMapReadyCallback callback = iterator.next(); - if (callback != null) { - // null checking required for #13279 - callback.onMapReady(mapboxMap); - } - iterator.remove(); - } - } - } - - void addOnMapReadyCallback(OnMapReadyCallback callback) { - onMapReadyCallbackList.add(callback); - } - - void onDestroy() { - onMapReadyCallbackList.clear(); - removeOnDidFinishLoadingStyleListener(this); - removeOnDidFinishRenderingFrameListener(this); - removeOnDidFinishLoadingMapListener(this); - removeOnCameraIsChangingListener(this); - removeOnCameraDidChangeListener(this); - removeOnDidFailLoadingMapListener(this); - } - - @Override - public void onDidFinishLoadingStyle() { - if (mapboxMap != null) { - mapboxMap.onFinishLoadingStyle(); - } - } - - @Override - public void onDidFailLoadingMap(String errorMessage) { - if (mapboxMap != null) { - mapboxMap.onFailLoadingStyle(); - } - } - - @Override - public void onDidFinishRenderingFrame(boolean fully) { - if (mapboxMap != null) { - mapboxMap.onUpdateFullyRendered(); - } - } - - @Override - public void onDidFinishLoadingMap() { - if (mapboxMap != null) { - mapboxMap.onUpdateRegionChange(); - } - } - - @Override - public void onCameraIsChanging() { - if (mapboxMap != null) { - mapboxMap.onUpdateRegionChange(); - } - } - - @Override - public void onCameraDidChange(boolean animated) { - if (mapboxMap != null) { - mapboxMap.onUpdateRegionChange(); - } - } - } - - /** - * Click event hook for providing a custom attribution dialog manager. - */ - private static class AttributionClickListener implements OnClickListener { - - @NonNull - private final AttributionDialogManager defaultDialogManager; - private UiSettings uiSettings; - - private AttributionClickListener(@NonNull Context context, @NonNull MapboxMap mapboxMap) { - this.defaultDialogManager = new AttributionDialogManager(context, mapboxMap); - this.uiSettings = mapboxMap.getUiSettings(); - } - - @Override - public void onClick(View v) { - getDialogManager().onClick(v); - } - - public void onStop() { - getDialogManager().onStop(); - } - - private AttributionDialogManager getDialogManager() { - AttributionDialogManager customDialogManager = uiSettings.getAttributionDialogManager(); - if (customDialogManager != null) { - return uiSettings.getAttributionDialogManager(); - } else { - return defaultDialogManager; - } - } - } - - /** - * Sets the strict mode that will throw the {@link com.mapbox.mapboxsdk.MapStrictModeException} - * whenever the map would fail silently otherwise. - * - * @param strictModeEnabled true to enable the strict mode, false otherwise - */ - public static void setMapStrictModeEnabled(boolean strictModeEnabled) { - MapStrictMode.setStrictModeEnabled(strictModeEnabled); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java deleted file mode 100644 index 8f4cb9438..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ /dev/null @@ -1,2425 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.Bundle; -import android.support.annotation.FloatRange; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.Size; -import android.support.annotation.UiThread; -import android.text.TextUtils; -import android.view.View; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.android.gestures.RotateGestureDetector; -import com.mapbox.android.gestures.ShoveGestureDetector; -import com.mapbox.android.gestures.StandardScaleGestureDetector; -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Geometry; -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.PolygonOptions; -import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.annotations.PolylineOptions; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.location.LocationComponent; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.offline.OfflineRegionDefinition; -import com.mapbox.mapboxsdk.style.expressions.Expression; - -import java.util.ArrayList; -import java.util.List; - -/** - * The general class to interact with in the Android Mapbox SDK. It exposes the entry point for all - * methods related to the MapView. You cannot instantiate {@link MapboxMap} object directly, rather, - * you must obtain one from the getMapAsync() method on a MapFragment or MapView that you have - * added to your application. - *

- * Note: Similar to a View object, a MapboxMap should only be read and modified from the main thread. - *

- */ -@UiThread -public final class MapboxMap { - - private static final String TAG = "Mbgl-MapboxMap"; - - private final NativeMap nativeMapView; - private final UiSettings uiSettings; - private final Projection projection; - private final Transform transform; - private final CameraChangeDispatcher cameraChangeDispatcher; - private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener; - private final List awaitingStyleGetters = new ArrayList<>(); - private final List developerAnimationStartedListeners; - - @Nullable - private Style.OnStyleLoaded styleLoadedCallback; - - private LocationComponent locationComponent; - private AnnotationManager annotationManager; - - @Nullable - private MapboxMap.OnFpsChangedListener onFpsChangedListener; - - @Nullable - private Style style; - - private boolean debugActive; - - MapboxMap(NativeMap map, Transform transform, UiSettings ui, Projection projection, - OnGesturesManagerInteractionListener listener, CameraChangeDispatcher cameraChangeDispatcher, - List developerAnimationStartedListeners) { - this.nativeMapView = map; - this.uiSettings = ui; - this.projection = projection; - this.transform = transform; - this.onGesturesManagerInteractionListener = listener; - this.cameraChangeDispatcher = cameraChangeDispatcher; - this.developerAnimationStartedListeners = developerAnimationStartedListeners; - } - - void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { - transform.initialise(this, options); - uiSettings.initialise(context, options); - - // Map configuration - setDebugActive(options.getDebugActive()); - setApiBaseUrl(options); - setPrefetchesTiles(options); - } - - /** - * Get the Style of the map asynchronously. - */ - public void getStyle(@NonNull Style.OnStyleLoaded onStyleLoaded) { - if (style != null && style.isFullyLoaded()) { - onStyleLoaded.onStyleLoaded(style); - } else { - awaitingStyleGetters.add(onStyleLoaded); - } - } - - /** - * Get the Style of the map. - *

- * Returns null when style is being loaded. - *

- * - * @return the style of the map - */ - @Nullable - public Style getStyle() { - if (style == null || !style.isFullyLoaded()) { - return null; - } else { - return style; - } - } - - /** - * Called when the hosting Activity/Fragment onStart() method is called. - */ - void onStart() { - locationComponent.onStart(); - } - - /** - * Called when the hosting Activity/Fragment onStop() method is called. - */ - void onStop() { - locationComponent.onStop(); - } - - /** - * Called when the hosting Activity/Fragment is going to be destroyed and map state needs to be saved. - * - * @param outState the bundle to save the state to. - */ - void onSaveInstanceState(@NonNull Bundle outState) { - outState.putParcelable(MapboxConstants.STATE_CAMERA_POSITION, transform.getCameraPosition()); - outState.putBoolean(MapboxConstants.STATE_DEBUG_ACTIVE, isDebugActive()); - uiSettings.onSaveInstanceState(outState); - } - - /** - * Called when the hosting Activity/Fragment is recreated and map state needs to be restored. - * - * @param savedInstanceState the bundle containing the saved state - */ - void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { - final CameraPosition cameraPosition = savedInstanceState.getParcelable(MapboxConstants.STATE_CAMERA_POSITION); - - uiSettings.onRestoreInstanceState(savedInstanceState); - - if (cameraPosition != null) { - moveCamera(CameraUpdateFactory.newCameraPosition( - new CameraPosition.Builder(cameraPosition).build()) - ); - } - - nativeMapView.setDebug(savedInstanceState.getBoolean(MapboxConstants.STATE_DEBUG_ACTIVE)); - } - - /** - * Called when the hosting Activity/Fragment onDestroy()/onDestroyView() method is called. - */ - void onDestroy() { - locationComponent.onDestroy(); - if (style != null) { - style.clear(); - } - cameraChangeDispatcher.onDestroy(); - } - - /** - * Called before the OnMapReadyCallback is invoked. - */ - void onPreMapReady() { - transform.invalidateCameraPosition(); - annotationManager.reloadMarkers(); - annotationManager.adjustTopOffsetPixels(this); - } - - /** - * Called when the OnMapReadyCallback has finished executing. - *

- * Invalidation of the camera position is required to update the added components in - * OnMapReadyCallback with the correct transformation. - *

- */ - void onPostMapReady() { - transform.invalidateCameraPosition(); - } - - /** - * Called when the map finished loading a style. - */ - void onFinishLoadingStyle() { - notifyStyleLoaded(); - } - - /** - * Called when the map failed loading a style. - */ - void onFailLoadingStyle() { - styleLoadedCallback = null; - } - - /** - * Called when the region is changing or has changed. - */ - void onUpdateRegionChange() { - annotationManager.update(); - } - - /** - * Called when the map frame is fully rendered. - */ - void onUpdateFullyRendered() { - CameraPosition cameraPosition = transform.invalidateCameraPosition(); - if (cameraPosition != null) { - uiSettings.update(cameraPosition); - } - } - - /** - * Experimental feature. Do not use. - */ - long getNativeMapPtr() { - return nativeMapView.getNativePtr(); - } - - // Style - - /** - * Sets tile pre-fetching zoom delta from MapboxOptions. - * - * @param options the options object - */ - private void setPrefetchesTiles(@NonNull MapboxMapOptions options) { - if (!options.getPrefetchesTiles()) { - setPrefetchZoomDelta(0); - } else { - setPrefetchZoomDelta(options.getPrefetchZoomDelta()); - } - } - - /** - * Enable or disable tile pre-fetching. Pre-fetching makes sure that a low-resolution - * tile is rendered as soon as possible at the expense of a little bandwidth. - * - * @param enable true to enable - * @deprecated Use {@link #setPrefetchZoomDelta(int)} instead. - */ - @Deprecated - public void setPrefetchesTiles(boolean enable) { - nativeMapView.setPrefetchTiles(enable); - } - - /** - * Check whether tile pre-fetching is enabled or not. - * - * @return true if enabled - * @see MapboxMap#setPrefetchesTiles(boolean) - * @deprecated Use {@link #getPrefetchZoomDelta()} instead. - */ - @Deprecated - public boolean getPrefetchesTiles() { - return nativeMapView.getPrefetchTiles(); - } - - /** - * Set the tile pre-fetching zoom delta. Pre-fetching makes sure that a low-resolution - * tile at the (current_zoom_level - delta) is rendered as soon as possible at the - * expense of a little bandwidth. - * Note: This operation will override the MapboxMapOptions#setPrefetchesTiles(boolean) - * Setting zoom delta to 0 will disable pre-fetching. - * Default zoom delta is 4. - * - * @param delta zoom delta - */ - public void setPrefetchZoomDelta(@IntRange(from = 0) int delta) { - nativeMapView.setPrefetchZoomDelta(delta); - } - - /** - * Check current pre-fetching zoom delta. - * - * @return current zoom delta. - * @see MapboxMap#setPrefetchZoomDelta(int) - */ - @IntRange(from = 0) - public int getPrefetchZoomDelta() { - return nativeMapView.getPrefetchZoomDelta(); - } - - // - // MinZoom - // - - /** - *

- * Sets the minimum zoom level the map can be displayed at. - *

- * - * @param minZoom The new minimum zoom level. - */ - public void setMinZoomPreference( - @FloatRange(from = MapboxConstants.MINIMUM_ZOOM, to = MapboxConstants.MAXIMUM_ZOOM) double minZoom) { - transform.setMinZoom(minZoom); - } - - /** - *

- * Gets the minimum zoom level the map can be displayed at. - *

- * - * @return The minimum zoom level. - */ - public double getMinZoomLevel() { - return transform.getMinZoom(); - } - - // - // MaxZoom - // - - /** - *

- * Sets the maximum zoom level the map can be displayed at. - *

- *

- * The default maximum zoomn level is 22. The upper bound for this value is 25.5. - *

- * - * @param maxZoom The new maximum zoom level. - */ - public void setMaxZoomPreference(@FloatRange(from = MapboxConstants.MINIMUM_ZOOM, - to = MapboxConstants.MAXIMUM_ZOOM) double maxZoom) { - transform.setMaxZoom(maxZoom); - } - - /** - *

- * Gets the maximum zoom level the map can be displayed at. - *

- * - * @return The maximum zoom level. - */ - public double getMaxZoomLevel() { - return transform.getMaxZoom(); - } - - // - // UiSettings - // - - /** - * Gets the user interface settings for the map. - * - * @return the UiSettings associated with this map - */ - @NonNull - public UiSettings getUiSettings() { - return uiSettings; - } - - // - // Projection - // - - /** - * Get the Projection object that you can use to convert between screen coordinates and latitude/longitude - * coordinates. - * - * @return the Projection associated with this map - */ - @NonNull - public Projection getProjection() { - return projection; - } - - // - // Camera API - // - - /** - * Cancels ongoing animations. - *

- * This invokes the {@link CancelableCallback} for ongoing camera updates. - *

- */ - public void cancelTransitions() { - transform.cancelTransitions(); - } - - /** - * Gets the current position of the camera. - * The CameraPosition returned is a snapshot of the current position, and will not automatically update when the - * camera moves. - * - * @return The current position of the Camera. - */ - @NonNull - public final CameraPosition getCameraPosition() { - return transform.getCameraPosition(); - } - - /** - * Repositions the camera according to the cameraPosition. - * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. - * See CameraUpdateFactory for a set of updates. - * - * @param cameraPosition the camera position to set - */ - public void setCameraPosition(@NonNull CameraPosition cameraPosition) { - moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition), null); - } - - /** - * Repositions the camera according to the instructions defined in the update. - * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. - * See CameraUpdateFactory for a set of updates. - * - * @param update The change that should be applied to the camera. - */ - public final void moveCamera(@NonNull CameraUpdate update) { - moveCamera(update, null); - } - - /** - * Repositions the camera according to the instructions defined in the update. - * The move is instantaneous, and a subsequent getCameraPosition() will reflect the new position. - * See CameraUpdateFactory for a set of updates. - * - * @param update The change that should be applied to the camera - * @param callback the callback to be invoked when an animation finishes or is canceled - */ - public final void moveCamera(@NonNull final CameraUpdate update, - @Nullable final MapboxMap.CancelableCallback callback) { - notifyDeveloperAnimationListeners(); - transform.moveCamera(MapboxMap.this, update, callback); - } - - /** - * Gradually move the camera by the default duration, zoom will not be affected unless specified - * within {@link CameraUpdate}. If {@link #getCameraPosition()} is called during the animation, - * it will return the current location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void easeCamera(@NonNull CameraUpdate update) { - easeCamera(update, MapboxConstants.ANIMATION_DURATION); - } - - /** - * Gradually move the camera by the default duration, zoom will not be affected unless specified - * within {@link CameraUpdate}. If {@link #getCameraPosition()} is called during the animation, - * it will return the current location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * by a later camera movement or a user gesture, onCancel() will be called. - * Do not update or ease the camera from within onCancel(). - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void easeCamera(@NonNull CameraUpdate update, @Nullable final MapboxMap.CancelableCallback callback) { - easeCamera(update, MapboxConstants.ANIMATION_DURATION, callback); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. If {@link #getCameraPosition()} is called - * during the animation, it will return the current location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void easeCamera(@NonNull CameraUpdate update, int durationMs) { - easeCamera(update, durationMs, null); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - *

- * Note that this will cancel location tracking mode if enabled. - *

- * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * by a later camera movement or a user gesture, onCancel() will be called. - * Do not update or ease the camera from within onCancel(). - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void easeCamera(@NonNull CameraUpdate update, int durationMs, - @Nullable final MapboxMap.CancelableCallback callback) { - easeCamera(update, durationMs, true, callback); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - *

- * Note that this will cancel location tracking mode if enabled. - *

- * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param easingInterpolator True for easing interpolator, false for linear. - */ - public final void easeCamera(@NonNull CameraUpdate update, int durationMs, boolean easingInterpolator) { - easeCamera(update, durationMs, easingInterpolator, null); - } - - /** - * Gradually move the camera by a specified duration in milliseconds, zoom will not be affected - * unless specified within {@link CameraUpdate}. A callback can be used to be notified when - * easing the camera stops. If {@link #getCameraPosition()} is called during the animation, it - * will return the current location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param easingInterpolator True for easing interpolator, false for linear. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * by a later camera movement or a user gesture, onCancel() will be called. - * Do not update or ease the camera from within onCancel(). - */ - public final void easeCamera(@NonNull final CameraUpdate update, - final int durationMs, - final boolean easingInterpolator, - @Nullable final MapboxMap.CancelableCallback callback) { - if (durationMs <= 0) { - throw new IllegalArgumentException("Null duration passed into easeCamera"); - } - notifyDeveloperAnimationListeners(); - transform.easeCamera(MapboxMap.this, update, durationMs, easingInterpolator, callback); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last the default amount of time. - * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location - * of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void animateCamera(@NonNull CameraUpdate update) { - animateCamera(update, MapboxConstants.ANIMATION_DURATION, null); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last the default amount of time. A - * callback can be used to be notified when animating the camera stops. During the animation, a - * call to {@link #getCameraPosition()} returns an intermediate location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param callback The callback to invoke from the main thread when the animation stops. If the - * animation completes normally, onFinish() is called; otherwise, onCancel() is - * called. Do not update or animate the camera from within onCancel(). - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void animateCamera(@NonNull CameraUpdate update, @Nullable MapboxMap.CancelableCallback callback) { - animateCamera(update, MapboxConstants.ANIMATION_DURATION, callback); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last a specified amount of time - * given in milliseconds. During the animation, a call to {@link #getCameraPosition()} returns - * an intermediate location of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void animateCamera(@NonNull CameraUpdate update, int durationMs) { - animateCamera(update, durationMs, null); - } - - /** - * Animate the camera to a new location defined within {@link CameraUpdate} using a transition - * animation that evokes powered flight. The animation will last a specified amount of time - * given in milliseconds. A callback can be used to be notified when animating the camera stops. - * During the animation, a call to {@link #getCameraPosition()} returns an intermediate location - * of the camera in flight. - * - * @param update The change that should be applied to the camera. - * @param durationMs The duration of the animation in milliseconds. This must be strictly - * positive, otherwise an IllegalArgumentException will be thrown. - * @param callback An optional callback to be notified from the main thread when the animation - * stops. If the animation stops due to its natural completion, the callback - * will be notified with onFinish(). If the animation stops due to interruption - * by a later camera movement or a user gesture, onCancel() will be called. - * Do not update or animate the camera from within onCancel(). If a callback - * isn't required, leave it as null. - * @see com.mapbox.mapboxsdk.camera.CameraUpdateFactory for a set of updates. - */ - public final void animateCamera(@NonNull final CameraUpdate update, final int durationMs, - @Nullable final MapboxMap.CancelableCallback callback) { - if (durationMs <= 0) { - throw new IllegalArgumentException("Null duration passed into animateCamera"); - } - notifyDeveloperAnimationListeners(); - transform.animateCamera(MapboxMap.this, update, durationMs, callback); - } - - /** - * Scrolls the camera over the map, shifting the center of view by the specified number of pixels in the x and y - * directions. - * - * @param x Amount of pixels to scroll to in x direction - * @param y Amount of pixels to scroll to in y direction - */ - public void scrollBy(float x, float y) { - scrollBy(x, y, 0); - } - - /** - * Scrolls the camera over the map, shifting the center of view by the specified number of pixels in the x and y - * directions. - * - * @param x Amount of pixels to scroll to in x direction - * @param y Amount of pixels to scroll to in y direction - * @param duration Amount of time the scrolling should take - */ - public void scrollBy(float x, float y, long duration) { - notifyDeveloperAnimationListeners(); - nativeMapView.moveBy(x, y, duration); - } - - // - // Reset North - // - - /** - * Resets the map view to face north. - */ - public void resetNorth() { - notifyDeveloperAnimationListeners(); - transform.resetNorth(); - } - - /** - * Transform the map bearing given a bearing, focal point coordinates, and a duration. - * - * @param bearing The bearing of the Map to be transformed to - * @param focalX The x coordinate of the focal point - * @param focalY The y coordinate of the focal point - * @param duration The duration of the transformation - */ - public void setFocalBearing(double bearing, float focalX, float focalY, long duration) { - notifyDeveloperAnimationListeners(); - transform.setBearing(bearing, focalX, focalY, duration); - } - - /** - * Returns the measured height of the Map. - * - * @return the height of the map - */ - public float getHeight() { - return projection.getHeight(); - } - - /** - * Returns the measured width of the Map. - * - * @return the width of the map - */ - public float getWidth() { - return projection.getWidth(); - } - - // - // Offline - // - - /** - * Loads a new style from the specified offline region definition and moves the map camera to that region. - * - * @param definition the offline region definition - * @see OfflineRegionDefinition - */ - public void setOfflineRegionDefinition(@NonNull OfflineRegionDefinition definition) { - setOfflineRegionDefinition(definition, null); - } - - /** - * Loads a new style from the specified offline region definition and moves the map camera to that region. - * - * @param definition the offline region definition - * @param callback the callback to be invoked when the style has loaded - * @see OfflineRegionDefinition - */ - public void setOfflineRegionDefinition(@NonNull OfflineRegionDefinition definition, - @Nullable Style.OnStyleLoaded callback) { - double minZoom = definition.getMinZoom(); - double maxZoom = definition.getMaxZoom(); - CameraPosition cameraPosition = new CameraPosition.Builder() - .target(definition.getBounds().getCenter()) - .zoom(minZoom) - .build(); - moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); - setMinZoomPreference(minZoom); - setMaxZoomPreference(maxZoom); - setStyle(new Style.Builder().fromUri(definition.getStyleURL()), callback); - } - - // - // Debug - // - - /** - * Returns whether the map debug information is currently shown. - * - * @return If true, map debug information is currently shown. - */ - public boolean isDebugActive() { - return debugActive; - } - - /** - *

- * Changes whether the map debug information is shown. - *

- * The default value is false. - * - * @param debugActive If true, map debug information is shown. - */ - public void setDebugActive(boolean debugActive) { - this.debugActive = debugActive; - nativeMapView.setDebug(debugActive); - } - - /** - *

- * Cycles through the map debug options. - *

- * The value of isDebugActive reflects whether there are - * any map debug options enabled or disabled. - * - * @see #isDebugActive() - */ - public void cycleDebugOptions() { - nativeMapView.cycleDebugOptions(); - this.debugActive = nativeMapView.getDebug(); - } - - // - // API endpoint config - // - - private void setApiBaseUrl(@NonNull MapboxMapOptions options) { - String apiBaseUrl = options.getApiBaseUrl(); - if (!TextUtils.isEmpty(apiBaseUrl)) { - nativeMapView.setApiBaseUrl(apiBaseUrl); - } - } - - // - // Styling - // - - /** - * Loads a new map style from the specified bundled style. - *

- * This method is asynchronous and will return before the style finishes loading. - * If you wish to wait for the map to finish loading, listen to the {@link MapView.OnDidFinishLoadingStyleListener} - * callback or use the {@link #setStyle(String, Style.OnStyleLoaded)} method instead. - *

- * If the style fails to load or an invalid style URL is set, the map view will become blank. - * An error message will be logged in the Android logcat and {@link MapView.OnDidFailLoadingMapListener} callback - * will be triggered. - * - * @param style The bundled style - * @see Style - */ - public void setStyle(@Style.StyleUrl String style) { - this.setStyle(style, null); - } - - /** - * Loads a new map style from the specified bundled style. - *

- * If the style fails to load or an invalid style URL is set, the map view will become blank. - * An error message will be logged in the Android logcat and {@link MapView.OnDidFailLoadingMapListener} callback - * will be triggered. - *

- * - * @param style The bundled style - * @param callback The callback to be invoked when the style has loaded - * @see Style - */ - public void setStyle(@Style.StyleUrl String style, final Style.OnStyleLoaded callback) { - this.setStyle(new Style.Builder().fromUri(style), callback); - } - - /** - * Loads a new map style from the specified builder. - *

- * If the builder fails to load, the map view will become blank. An error message will be logged in the Android logcat - * and {@link MapView.OnDidFailLoadingMapListener} callback will be triggered. If you wish to wait for the map to - * finish loading, listen to the {@link MapView.OnDidFinishLoadingStyleListener} callback or use the - * {@link #setStyle(String, Style.OnStyleLoaded)} instead. - *

- * - * @param builder The style builder - * @see Style - */ - public void setStyle(Style.Builder builder) { - this.setStyle(builder, null); - } - - /** - * Loads a new map style from the specified builder. - *

- * If the builder fails to load, the map view will become blank. An error message will be logged in the Android logcat - * and {@link MapView.OnDidFailLoadingMapListener} callback will be triggered. - *

- * - * @param builder The style builder - * @param callback The callback to be invoked when the style has loaded - * @see Style - */ - public void setStyle(Style.Builder builder, final Style.OnStyleLoaded callback) { - styleLoadedCallback = callback; - locationComponent.onStartLoadingMap(); - if (style != null) { - style.clear(); - } - - style = builder.build(nativeMapView); - if (!TextUtils.isEmpty(builder.getUri())) { - nativeMapView.setStyleUri(builder.getUri()); - } else if (!TextUtils.isEmpty(builder.getJson())) { - nativeMapView.setStyleJson(builder.getJson()); - } else { - // user didn't provide a `from` component, load a blank style instead - nativeMapView.setStyleJson(Style.EMPTY_JSON); - } - } - - void notifyStyleLoaded() { - if (nativeMapView.isDestroyed()) { - return; - } - - if (style != null) { - style.onDidFinishLoadingStyle(); - locationComponent.onFinishLoadingStyle(); - - // notify the listener provided with the style setter - if (styleLoadedCallback != null) { - styleLoadedCallback.onStyleLoaded(style); - } - - // notify style getters - for (Style.OnStyleLoaded styleGetter : awaitingStyleGetters) { - styleGetter.onStyleLoaded(style); - } - } else { - MapStrictMode.strictModeViolation("No style to provide."); - } - styleLoadedCallback = null; - awaitingStyleGetters.clear(); - } - - // - // Annotations - // - - /** - *

- * Adds a marker to this map. - *

- * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. - * - * @param markerOptions A marker options object that defines how to render the marker - * @return The {@code Marker} that was added to the map - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public Marker addMarker(@NonNull MarkerOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this); - } - - /** - *

- * Adds a marker to this map. - *

- * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. - * - * @param markerOptions A marker options object that defines how to render the marker - * @return The {@code Marker} that was added to the map - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public Marker addMarker(@NonNull BaseMarkerOptions markerOptions) { - return annotationManager.addMarker(markerOptions, this); - } - - /** - *

- * Adds multiple markers to this map. - *

- * The marker's icon is rendered on the map at the location {@code Marker.position}. - * If {@code Marker.title} is defined, the map shows an info box with the marker's title and snippet. - * - * @param markerOptionsList A list of marker options objects that defines how to render the markers - * @return A list of the {@code Marker}s that were added to the map - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List addMarkers(@NonNull List markerOptionsList) { - return annotationManager.addMarkers(markerOptionsList, this); - } - - /** - *

- * Updates a marker on this map. Does nothing if the marker isn't already added. - *

- * - * @param updatedMarker An updated marker object - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void updateMarker(@NonNull Marker updatedMarker) { - annotationManager.updateMarker(updatedMarker, this); - } - - /** - * Adds a polyline to this map. - * - * @param polylineOptions A polyline options object that defines how to render the polyline - * @return The {@code Polyine} that was added to the map - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public Polyline addPolyline(@NonNull PolylineOptions polylineOptions) { - return annotationManager.addPolyline(polylineOptions, this); - } - - /** - * Adds multiple polylines to this map. - * - * @param polylineOptionsList A list of polyline options objects that defines how to render the polylines. - * @return A list of the {@code Polyline}s that were added to the map. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List addPolylines(@NonNull List polylineOptionsList) { - return annotationManager.addPolylines(polylineOptionsList, this); - } - - /** - * Update a polyline on this map. - * - * @param polyline An updated polyline object. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void updatePolyline(@NonNull Polyline polyline) { - annotationManager.updatePolyline(polyline); - } - - /** - * Adds a polygon to this map. - * - * @param polygonOptions A polygon options object that defines how to render the polygon. - * @return The {@code Polygon} that was added to the map. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public Polygon addPolygon(@NonNull PolygonOptions polygonOptions) { - return annotationManager.addPolygon(polygonOptions, this); - } - - /** - * Adds multiple polygons to this map. - * - * @param polygonOptionsList A list of polygon options objects that defines how to render the polygons - * @return A list of the {@code Polygon}s that were added to the map - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List addPolygons(@NonNull List polygonOptionsList) { - return annotationManager.addPolygons(polygonOptionsList, this); - } - - /** - * Update a polygon on this map. - * - * @param polygon An updated polygon object - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void updatePolygon(@NonNull Polygon polygon) { - annotationManager.updatePolygon(polygon); - } - - /** - *

- * Convenience method for removing a Marker from the map. - *

- * Calls removeAnnotation() internally. - * - * @param marker Marker to remove - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removeMarker(@NonNull Marker marker) { - annotationManager.removeAnnotation(marker); - } - - /** - *

- * Convenience method for removing a Polyline from the map. - *

- * Calls removeAnnotation() internally. - * - * @param polyline Polyline to remove - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removePolyline(@NonNull Polyline polyline) { - annotationManager.removeAnnotation(polyline); - } - - /** - *

- * Convenience method for removing a Polygon from the map. - *

- * Calls removeAnnotation() internally. - * - * @param polygon Polygon to remove - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removePolygon(@NonNull Polygon polygon) { - annotationManager.removeAnnotation(polygon); - } - - /** - * Removes an annotation from the map. - * - * @param annotation The annotation object to remove. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removeAnnotation(@NonNull Annotation annotation) { - annotationManager.removeAnnotation(annotation); - } - - /** - * Removes an annotation from the map - * - * @param id The identifier associated to the annotation to be removed - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removeAnnotation(long id) { - annotationManager.removeAnnotation(id); - } - - /** - * Removes multiple annotations from the map. - * - * @param annotationList A list of annotation objects to remove. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removeAnnotations(@NonNull List annotationList) { - annotationManager.removeAnnotations(annotationList); - } - - /** - * Removes all annotations from the map. - * - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void removeAnnotations() { - annotationManager.removeAnnotations(); - } - - /** - * Removes all markers, polylines, polygons, overlays, etc from the map. - * - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void clear() { - annotationManager.removeAnnotations(); - } - - /** - * Return a annotation based on its id. - * - * @param id the id used to look up an annotation - * @return An annotation with a matched id, null is returned if no match was found - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @Nullable - public Annotation getAnnotation(long id) { - return annotationManager.getAnnotation(id); - } - - /** - * Returns a list of all the annotations on the map. - * - * @return A list of all the annotation objects. The returned object is a copy so modifying this - * list will not update the map - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List getAnnotations() { - return annotationManager.getAnnotations(); - } - - /** - * Returns a list of all the markers on the map. - * - * @return A list of all the markers objects. The returned object is a copy so modifying this - * list will not update the map. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List getMarkers() { - return annotationManager.getMarkers(); - } - - /** - * Returns a list of all the polygons on the map. - * - * @return A list of all the polygon objects. The returned object is a copy so modifying this - * list will not update the map. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List getPolygons() { - return annotationManager.getPolygons(); - } - - /** - * Returns a list of all the polylines on the map. - * - * @return A list of all the polylines objects. The returned object is a copy so modifying this - * list will not update the map. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List getPolylines() { - return annotationManager.getPolylines(); - } - - /** - * Sets a callback that's invoked when the user clicks on a marker. - * - * @param listener The callback that's invoked when the user clicks on a marker. - * To unset the callback, use null. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void setOnMarkerClickListener(@Nullable OnMarkerClickListener listener) { - annotationManager.setOnMarkerClickListener(listener); - } - - /** - * Sets a callback that's invoked when the user clicks on a polygon. - * - * @param listener The callback that's invoked when the user clicks on a polygon. - * To unset the callback, use null. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void setOnPolygonClickListener(@Nullable OnPolygonClickListener listener) { - annotationManager.setOnPolygonClickListener(listener); - } - - /** - * Sets a callback that's invoked when the user clicks on a polyline. - * - * @param listener The callback that's invoked when the user clicks on a polyline. - * To unset the callback, use null. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void setOnPolylineClickListener(@Nullable OnPolylineClickListener listener) { - annotationManager.setOnPolylineClickListener(listener); - } - - /** - *

- * Selects a marker. The selected marker will have it's info window opened. - * Any other open info windows will be closed unless isAllowConcurrentMultipleOpenInfoWindows() - * is true. - *

- * Selecting an already selected marker will have no effect. - * - * @param marker The marker to select. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void selectMarker(@NonNull Marker marker) { - if (marker == null) { - Logger.w(TAG, "marker was null, so just returning"); - return; - } - annotationManager.selectMarker(marker); - } - - /** - * Deselects any currently selected marker. All markers will have it's info window closed. - * - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void deselectMarkers() { - annotationManager.deselectMarkers(); - } - - /** - * Deselects a currently selected marker. The selected marker will have it's info window closed. - * - * @param marker the marker to deselect - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void deselectMarker(@NonNull Marker marker) { - annotationManager.deselectMarker(marker); - } - - /** - * Gets the currently selected marker. - * - * @return The currently selected marker. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @NonNull - public List getSelectedMarkers() { - return annotationManager.getSelectedMarkers(); - } - - // - // InfoWindow - // - - /** - *

- * Sets a custom renderer for the contents of info window. - *

- * When set your callback is invoked when an info window is about to be shown. By returning - * a custom {@link View}, the default info window will be replaced. - * - * @param infoWindowAdapter The callback to be invoked when an info window will be shown. - * To unset the callback, use null. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void setInfoWindowAdapter(@Nullable InfoWindowAdapter infoWindowAdapter) { - annotationManager.getInfoWindowManager().setInfoWindowAdapter(infoWindowAdapter); - } - - /** - * Gets the callback to be invoked when an info window will be shown. - * - * @return The callback to be invoked when an info window will be shown. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - @Nullable - public InfoWindowAdapter getInfoWindowAdapter() { - return annotationManager.getInfoWindowManager().getInfoWindowAdapter(); - } - - /** - * Changes whether the map allows concurrent multiple infowindows to be shown. - * - * @param allow If true, map allows concurrent multiple infowindows to be shown. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public void setAllowConcurrentMultipleOpenInfoWindows(boolean allow) { - annotationManager.getInfoWindowManager().setAllowConcurrentMultipleOpenInfoWindows(allow); - } - - /** - * Returns whether the map allows concurrent multiple infowindows to be shown. - * - * @return If true, map allows concurrent multiple infowindows to be shown. - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public boolean isAllowConcurrentMultipleOpenInfoWindows() { - return annotationManager.getInfoWindowManager().isAllowConcurrentMultipleOpenInfoWindows(); - } - - // - // LatLngBounds - // - - /** - * Sets a LatLngBounds that constraints map transformations to this bounds. - *

- * Set to null to clear current bounds, newly set bounds will override previously set bounds. - *

- * - * @param latLngBounds the bounds to constrain the map with - */ - public void setLatLngBoundsForCameraTarget(@Nullable LatLngBounds latLngBounds) { - nativeMapView.setLatLngBounds(latLngBounds); - } - - - /** - * Get a camera position that fits a provided bounds and the current camera tilt and bearing. - * - * @param latLngBounds the bounds to set the map with - * @return the camera position that fits the bounds - */ - @Nullable - public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds) { - // we use current camera tilt value to provide expected transformations as #11993 - return getCameraForLatLngBounds(latLngBounds, new int[] {0, 0, 0, 0}); - } - - - /** - * Get a camera position that fits a provided bounds and padding and the current camera tilt and bearing. - * - * @param latLngBounds the bounds to set the map with - * @param padding the padding to apply to the bounds - * @return the camera position that fits the bounds and padding - */ - @Nullable - public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, - @NonNull @Size(value = 4) int[] padding) { - // we use current camera tilt/bearing value to provide expected transformations as #11993 - return getCameraForLatLngBounds(latLngBounds, padding, transform.getRawBearing(), transform.getTilt()); - } - - - /** - * Get a camera position that fits a provided bounds, bearing and tilt. - * - * @param latLngBounds the bounds to set the map with - * @param bearing the bearing to transform the camera position with - * @param tilt to transform the camera position with - * @return the camera position that fits the bounds and given bearing and tilt - */ - @Nullable - public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, - @FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, - to = MapboxConstants.MAXIMUM_DIRECTION) double bearing, - @FloatRange(from = MapboxConstants.MINIMUM_TILT, - to = MapboxConstants.MAXIMUM_TILT) double tilt) { - return getCameraForLatLngBounds(latLngBounds, new int[] {0, 0, 0, 0}, bearing, tilt); - } - - - /** - * Get a camera position that fits a provided bounds, padding, bearing and tilt. - * - * @param latLngBounds the bounds to set the map with - * @param padding the padding to apply to the bounds - * @param bearing the bearing to transform the camera position with - * @param tilt to transform the camera position with - * @return the camera position that fits the bounds, bearing and tilt - */ - @Nullable - public CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds latLngBounds, - @NonNull @Size(value = 4) int[] padding, - @FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, - to = MapboxConstants.MAXIMUM_DIRECTION) double bearing, - @FloatRange(from = MapboxConstants.MINIMUM_TILT, - to = MapboxConstants.MAXIMUM_TILT) double tilt) { - return nativeMapView.getCameraForLatLngBounds(latLngBounds, padding, bearing, tilt); - } - - /** - * Get a camera position that fits a provided shape. - * - * @param geometry the geometry to wraps the map with - * @return the camera position that fits the geometry inside - */ - @Nullable - public CameraPosition getCameraForGeometry(@NonNull Geometry geometry) { - // we use current camera tilt value to provide expected transformations as #11993 - return getCameraForGeometry(geometry, new int[] {0, 0, 0, 0}); - } - - /** - * Get a camera position that fits a provided shape and padding. - * - * @param geometry the geometry to wraps the map with - * @param padding the padding to apply to the bounds - * @return the camera position that fits the geometry inside and padding - */ - @Nullable - public CameraPosition getCameraForGeometry(@NonNull Geometry geometry, - @NonNull @Size(value = 4) int[] padding) { - // we use current camera tilt/bearing value to provide expected transformations as #11993 - return getCameraForGeometry(geometry, padding, transform.getBearing(), transform.getTilt()); - } - - /** - * Get a camera position that fits a provided shape with a given bearing and tilt. - * - * @param geometry the geometry to wraps the map with - * @param bearing the bearing at which to compute the geometry's bounds - * @param tilt the tilt at which to compute the geometry's bounds - * @return the camera position that the geometry inside with bearing and tilt - */ - @Nullable - public CameraPosition getCameraForGeometry(@NonNull Geometry geometry, - @FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, - to = MapboxConstants.MAXIMUM_DIRECTION) double bearing, - @FloatRange(from = MapboxConstants.MINIMUM_TILT, - to = MapboxConstants.MAXIMUM_TILT) double tilt) { - return getCameraForGeometry(geometry, new int[] {0, 0, 0, 0}, bearing, tilt); - } - - /** - * Get a camera position that fits a provided shape with a given padding, bearing and tilt. - * - * @param geometry the geometry to wraps the map with - * @param padding the padding to apply to the bounds - * @param bearing the bearing at which to compute the geometry's bounds - * @param tilt the tilt at which to compute the geometry's bounds - * @return the camera position that fits the geometry inside with padding, bearing and tilt - */ - @Nullable - public CameraPosition getCameraForGeometry(@NonNull Geometry geometry, - @NonNull @Size(value = 4) int[] padding, - @FloatRange(from = MapboxConstants.MINIMUM_DIRECTION, - to = MapboxConstants.MAXIMUM_DIRECTION) double bearing, - @FloatRange(from = MapboxConstants.MINIMUM_TILT, - to = MapboxConstants.MAXIMUM_TILT) double tilt) { - return nativeMapView.getCameraForGeometry(geometry, padding, bearing, tilt); - } - - // - // Padding - // - - /** - *

- * Sets the distance from the edges of the map view's frame to the edges of the map - * view's logical viewport. - *

- *

- * When the value of this property is equal to {0,0,0,0}, viewport - * properties such as 'centerCoordinate' assume a viewport that matches the map - * view's frame. Otherwise, those properties are inset, excluding part of the - * frame from the viewport. For instance, if the only the top edge is inset, the - * map center is effectively shifted downward. - *

- *

- * This method sets the padding "lazily". - * This means that the padding is going to be applied with the next camera transformation. - * To apply the padding immediately use {@link CameraPosition.Builder#padding(double, double, double, double)} - * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)}. - *

- * - * @param left The left margin in pixels. - * @param top The top margin in pixels. - * @param right The right margin in pixels. - * @param bottom The bottom margin in pixels. - * @deprecated Use {@link CameraPosition.Builder#padding(double, double, double, double)} - * or {@link CameraUpdateFactory#paddingTo(double, double, double, double)} instead. - */ - @Deprecated - public void setPadding(int left, int top, int right, int bottom) { - // TODO padding should be passed as doubles - projection.setContentPadding(new int[] {left, top, right, bottom}); - uiSettings.invalidate(); - } - - /** - * Returns the current configured content padding on map view. This might return the currently visible padding - * or the padding cached but not yet applied by {@link #setPadding(int, int, int, int)}. - * - * @return An array with length 4 in the LTRB order. - * @deprecated Use {@link CameraPosition#padding} instead. - */ - @Deprecated - @NonNull - public int[] getPadding() { - // TODO this should return double[] (semver major change) - return projection.getContentPadding(); - } - - // - // Map events - // - - /** - * Adds a callback that is invoked when camera movement has ended. - * - * @param listener the listener to notify - */ - public void addOnCameraIdleListener(@NonNull OnCameraIdleListener listener) { - cameraChangeDispatcher.addOnCameraIdleListener(listener); - } - - /** - * Removes a callback that is invoked when camera movement has ended. - * - * @param listener the listener to remove - */ - public void removeOnCameraIdleListener(@NonNull OnCameraIdleListener listener) { - cameraChangeDispatcher.removeOnCameraIdleListener(listener); - } - - /** - * Adds a callback that is invoked when camera movement was cancelled. - * - * @param listener the listener to notify - */ - public void addOnCameraMoveCancelListener(@NonNull OnCameraMoveCanceledListener listener) { - cameraChangeDispatcher.addOnCameraMoveCancelListener(listener); - } - - /** - * Removes a callback that is invoked when camera movement was cancelled. - * - * @param listener the listener to remove - */ - public void removeOnCameraMoveCancelListener(@NonNull OnCameraMoveCanceledListener listener) { - cameraChangeDispatcher.removeOnCameraMoveCancelListener(listener); - } - - /** - * Adds a callback that is invoked when camera movement has started. - * - * @param listener the listener to notify - */ - public void addOnCameraMoveStartedListener(@NonNull OnCameraMoveStartedListener listener) { - cameraChangeDispatcher.addOnCameraMoveStartedListener(listener); - } - - /** - * Removes a callback that is invoked when camera movement has started. - * - * @param listener the listener to remove - */ - public void removeOnCameraMoveStartedListener(@NonNull OnCameraMoveStartedListener listener) { - cameraChangeDispatcher.removeOnCameraMoveStartedListener(listener); - } - - /** - * Adds a callback that is invoked when camera position changes. - * - * @param listener the listener to notify - */ - public void addOnCameraMoveListener(@NonNull OnCameraMoveListener listener) { - cameraChangeDispatcher.addOnCameraMoveListener(listener); - } - - /** - * Removes a callback that is invoked when camera position changes. - * - * @param listener the listener to remove - */ - public void removeOnCameraMoveListener(@NonNull OnCameraMoveListener listener) { - cameraChangeDispatcher.removeOnCameraMoveListener(listener); - } - - /** - * Sets a callback that's invoked on every frame rendered to the map view. - * - * @param listener The callback that's invoked on every frame rendered to the map view. - * To unset the callback, use null. - */ - public void setOnFpsChangedListener(@Nullable OnFpsChangedListener listener) { - onFpsChangedListener = listener; - nativeMapView.setOnFpsChangedListener(listener); - } - - // used by MapView - @Nullable - OnFpsChangedListener getOnFpsChangedListener() { - return onFpsChangedListener; - } - - /** - * Adds a callback that's invoked when the map is flinged. - * - * @param listener The callback that's invoked when the map is flinged. - */ - public void addOnFlingListener(@NonNull OnFlingListener listener) { - onGesturesManagerInteractionListener.onAddFlingListener(listener); - } - - /** - * Removes a callback that's invoked when the map is flinged. - * - * @param listener The callback that's invoked when the map is flinged. - */ - public void removeOnFlingListener(@NonNull OnFlingListener listener) { - onGesturesManagerInteractionListener.onRemoveFlingListener(listener); - } - - /** - * Adds a callback that's invoked when the map is moved. - * - * @param listener The callback that's invoked when the map is moved. - */ - public void addOnMoveListener(@NonNull OnMoveListener listener) { - onGesturesManagerInteractionListener.onAddMoveListener(listener); - } - - /** - * Removes a callback that's invoked when the map is moved. - * - * @param listener The callback that's invoked when the map is moved. - */ - public void removeOnMoveListener(@NonNull OnMoveListener listener) { - onGesturesManagerInteractionListener.onRemoveMoveListener(listener); - } - - /** - * Adds a callback that's invoked when the map is rotated. - * - * @param listener The callback that's invoked when the map is rotated. - */ - public void addOnRotateListener(@NonNull OnRotateListener listener) { - onGesturesManagerInteractionListener.onAddRotateListener(listener); - } - - /** - * Removes a callback that's invoked when the map is rotated. - * - * @param listener The callback that's invoked when the map is rotated. - */ - public void removeOnRotateListener(@NonNull OnRotateListener listener) { - onGesturesManagerInteractionListener.onRemoveRotateListener(listener); - } - - /** - * Adds a callback that's invoked when the map is scaled. - * - * @param listener The callback that's invoked when the map is scaled. - */ - public void addOnScaleListener(@NonNull OnScaleListener listener) { - onGesturesManagerInteractionListener.onAddScaleListener(listener); - } - - /** - * Removes a callback that's invoked when the map is scaled. - * - * @param listener The callback that's invoked when the map is scaled. - */ - public void removeOnScaleListener(@NonNull OnScaleListener listener) { - onGesturesManagerInteractionListener.onRemoveScaleListener(listener); - } - - /** - * Adds a callback that's invoked when the map is tilted. - * - * @param listener The callback that's invoked when the map is tilted. - */ - public void addOnShoveListener(@NonNull OnShoveListener listener) { - onGesturesManagerInteractionListener.onAddShoveListener(listener); - } - - /** - * Remove a callback that's invoked when the map is tilted. - * - * @param listener The callback that's invoked when the map is tilted. - */ - public void removeOnShoveListener(@NonNull OnShoveListener listener) { - onGesturesManagerInteractionListener.onRemoveShoveListener(listener); - } - - /** - * Sets a custom {@link AndroidGesturesManager} to handle {@link android.view.MotionEvent}s - * registered by the {@link MapView}. - * - * @param androidGesturesManager Gestures manager that interprets gestures based on the motion events. - * @param attachDefaultListeners If true, pre-defined listeners will be attach - * to change map based on {@link AndroidGesturesManager} callbacks. - * @param setDefaultMutuallyExclusives If true, pre-defined mutually exclusive gesture sets - * will be added to the passed gestures manager. - * @see mapbox-gestures-android library - */ - public void setGesturesManager(@NonNull AndroidGesturesManager androidGesturesManager, boolean attachDefaultListeners, - boolean setDefaultMutuallyExclusives) { - onGesturesManagerInteractionListener.setGesturesManager( - androidGesturesManager, attachDefaultListeners, setDefaultMutuallyExclusives); - } - - /** - * Get current {@link AndroidGesturesManager} that handles {@link android.view.MotionEvent}s - * registered by the {@link MapView} - * - * @return Current gestures manager. - */ - @NonNull - public AndroidGesturesManager getGesturesManager() { - return onGesturesManagerInteractionListener.getGesturesManager(); - } - - /** - * Interrupts any ongoing gesture velocity animations. - */ - public void cancelAllVelocityAnimations() { - onGesturesManagerInteractionListener.cancelAllVelocityAnimations(); - } - - /** - * Adds a callback that's invoked when the user clicks on the map view. - * - * @param listener The callback that's invoked when the user clicks on the map view. - */ - public void addOnMapClickListener(@NonNull OnMapClickListener listener) { - onGesturesManagerInteractionListener.onAddMapClickListener(listener); - } - - /** - * Removes a callback that's invoked when the user clicks on the map view. - * - * @param listener The callback that's invoked when the user clicks on the map view. - */ - public void removeOnMapClickListener(@NonNull OnMapClickListener listener) { - onGesturesManagerInteractionListener.onRemoveMapClickListener(listener); - } - - /** - * Adds a callback that's invoked when the user long clicks on the map view. - * - * @param listener The callback that's invoked when the user long clicks on the map view. - */ - public void addOnMapLongClickListener(@NonNull OnMapLongClickListener listener) { - onGesturesManagerInteractionListener.onAddMapLongClickListener(listener); - } - - /** - * Removes a callback that's invoked when the user long clicks on the map view. - * - * @param listener The callback that's invoked when the user long clicks on the map view. - */ - public void removeOnMapLongClickListener(@NonNull OnMapLongClickListener listener) { - onGesturesManagerInteractionListener.onRemoveMapLongClickListener(listener); - } - - /** - * Sets a callback that's invoked when the user clicks on an info window. - * - * @param listener The callback that's invoked when the user clicks on an info window. - * To unset the callback, use null. - */ - public void setOnInfoWindowClickListener(@Nullable OnInfoWindowClickListener listener) { - annotationManager.getInfoWindowManager().setOnInfoWindowClickListener(listener); - } - - /** - * Return the InfoWindow click listener - * - * @return Current active InfoWindow Click Listener - */ - @Nullable - public OnInfoWindowClickListener getOnInfoWindowClickListener() { - return annotationManager.getInfoWindowManager().getOnInfoWindowClickListener(); - } - - /** - * Sets a callback that's invoked when a marker's info window is long pressed. - * - * @param listener The callback that's invoked when a marker's info window is long pressed. To unset the callback, - * use null. - */ - public void setOnInfoWindowLongClickListener(@Nullable OnInfoWindowLongClickListener - listener) { - annotationManager.getInfoWindowManager().setOnInfoWindowLongClickListener(listener); - } - - /** - * Return the InfoWindow long click listener - * - * @return Current active InfoWindow long Click Listener - */ - @Nullable - public OnInfoWindowLongClickListener getOnInfoWindowLongClickListener() { - return annotationManager.getInfoWindowManager().getOnInfoWindowLongClickListener(); - } - - /** - * Set an callback to be invoked when an InfoWindow closes. - * - * @param listener callback invoked when an InfoWindow closes - */ - public void setOnInfoWindowCloseListener(@Nullable OnInfoWindowCloseListener listener) { - annotationManager.getInfoWindowManager().setOnInfoWindowCloseListener(listener); - } - - /** - * Return the InfoWindow close listener - * - * @return Current active InfoWindow Close Listener - */ - @Nullable - public OnInfoWindowCloseListener getOnInfoWindowCloseListener() { - return annotationManager.getInfoWindowManager().getOnInfoWindowCloseListener(); - } - - // - // Invalidate - // - - /** - * Takes a snapshot of the map. - * - * @param callback Callback method invoked when the snapshot is taken. - */ - public void snapshot(@NonNull SnapshotReadyCallback callback) { - nativeMapView.addSnapshotCallback(callback); - } - - /** - * Queries the map for rendered features. - *

- * Returns an empty list if either the map or underlying render surface has been destroyed. - *

- * - * @param coordinates the point to query - * @param layerIds optionally - only query these layers - * @return the list of feature - */ - @NonNull - public List queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds, null); - } - - /** - * Queries the map for rendered features - *

- * Returns an empty list if either the map or underlying render surface has been destroyed. - *

- * - * @param coordinates the point to query - * @param filter filters the returned features with an expression - * @param layerIds optionally - only query these layers - * @return the list of feature - */ - @NonNull - public List queryRenderedFeatures(@NonNull PointF coordinates, - @Nullable Expression filter, - @Nullable String... layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter); - } - - /** - * Queries the map for rendered features - *

- * Returns an empty list if either the map or underlying render surface has been destroyed. - *

- * - * @param coordinates the box to query - * @param layerIds optionally - only query these layers - * @return the list of feature - */ - @NonNull - public List queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds, null); - } - - /** - * Queries the map for rendered features - *

- * Returns an empty list if either the map or underlying render surface has been destroyed. - *

- * - * @param coordinates the box to query - * @param filter filters the returned features with an expression - * @param layerIds optionally - only query these layers - * @return the list of feature - */ - @NonNull - public List queryRenderedFeatures(@NonNull RectF coordinates, - @Nullable Expression filter, - @Nullable String... layerIds) { - return nativeMapView.queryRenderedFeatures(coordinates, layerIds, filter); - } - - // - // LocationComponent - // - - void injectLocationComponent(LocationComponent locationComponent) { - this.locationComponent = locationComponent; - } - - void injectAnnotationManager(AnnotationManager annotationManager) { - this.annotationManager = annotationManager.bind(this); - } - - /** - * Returns the {@link LocationComponent} that can be used to display user's location on the map. - *

- * Use {@link LocationComponent#activateLocationComponent(Context, Style)} or any overload to activate the component, - * then, enable it with {@link LocationComponent#setLocationComponentEnabled(boolean)}. - *

- * You can customize the location icon and more with {@link com.mapbox.mapboxsdk.location.LocationComponentOptions}. - * - * @return the Location Component - */ - @NonNull - public LocationComponent getLocationComponent() { - return locationComponent; - } - - // - // Interfaces - // - - /** - * Interface definition for a callback to be invoked when the map is flinged. - * - * @see MapboxMap#addOnFlingListener(OnFlingListener) - */ - public interface OnFlingListener { - /** - * Called when the map is flinged. - */ - void onFling(); - } - - /** - * Interface definition for a callback to be invoked when the map is moved. - * - * @see MapboxMap#addOnMoveListener(OnMoveListener) - */ - public interface OnMoveListener { - void onMoveBegin(@NonNull MoveGestureDetector detector); - - void onMove(@NonNull MoveGestureDetector detector); - - void onMoveEnd(@NonNull MoveGestureDetector detector); - } - - /** - * Interface definition for a callback to be invoked when the map is rotated. - * - * @see MapboxMap#addOnRotateListener(OnRotateListener) - */ - public interface OnRotateListener { - void onRotateBegin(@NonNull RotateGestureDetector detector); - - void onRotate(@NonNull RotateGestureDetector detector); - - void onRotateEnd(@NonNull RotateGestureDetector detector); - } - - /** - * Interface definition for a callback to be invoked when the map is scaled. - * - * @see MapboxMap#addOnScaleListener(OnScaleListener) - */ - public interface OnScaleListener { - void onScaleBegin(@NonNull StandardScaleGestureDetector detector); - - void onScale(@NonNull StandardScaleGestureDetector detector); - - void onScaleEnd(@NonNull StandardScaleGestureDetector detector); - } - - /** - * Interface definition for a callback to be invoked when the map is tilted. - * - * @see MapboxMap#addOnShoveListener(OnShoveListener) - */ - public interface OnShoveListener { - void onShoveBegin(@NonNull ShoveGestureDetector detector); - - void onShove(@NonNull ShoveGestureDetector detector); - - void onShoveEnd(@NonNull ShoveGestureDetector detector); - } - - /** - * Interface definition for a callback to be invoked for when the camera motion starts. - */ - public interface OnCameraMoveStartedListener { - int REASON_API_GESTURE = 1; - int REASON_DEVELOPER_ANIMATION = 2; - int REASON_API_ANIMATION = 3; - - /** - * Called when the camera starts moving after it has been idle or when the reason for camera motion has changed. - * - * @param reason the reason for the camera change - */ - void onCameraMoveStarted(int reason); - } - - /** - * Interface definition for a callback to be invoked for when the camera changes position. - */ - public interface OnCameraMoveListener { - /** - * Called repeatedly as the camera continues to move after an onCameraMoveStarted call. - * This may be called as often as once every frame and should not perform expensive operations. - */ - void onCameraMove(); - } - - /** - * Interface definition for a callback to be invoked for when the camera's motion has been stopped or when the camera - * starts moving for a new reason. - */ - public interface OnCameraMoveCanceledListener { - /** - * Called when the developer explicitly calls the cancelTransitions() method or if the reason for camera motion has - * changed before the onCameraIdle had a chance to fire after the previous animation. - * Do not update or animate the camera from within this method. - */ - void onCameraMoveCanceled(); - } - - /** - * Interface definition for a callback to be invoked for when camera movement has ended. - */ - public interface OnCameraIdleListener { - /** - * Called when camera movement has ended. - */ - void onCameraIdle(); - } - - /** - * Interface definition for a callback to be invoked for when the compass is animating. - */ - public interface OnCompassAnimationListener { - /** - * Called repeatedly as the compass continues to move after clicking on it. - */ - void onCompassAnimation(); - - /** - * Called when compass animation has ended. - */ - void onCompassAnimationFinished(); - } - - /** - * Interface definition for a callback to be invoked when a frame is rendered to the map view. - * - * @see MapboxMap#setOnFpsChangedListener(OnFpsChangedListener) - */ - public interface OnFpsChangedListener { - /** - * Called for every frame rendered to the map view. - * - * @param fps The average number of frames rendered over the last second. - */ - void onFpsChanged(double fps); - } - - /** - * Interface definition for a callback to be invoked when a user registers an listener that is - * related to touch and click events. - */ - interface OnGesturesManagerInteractionListener { - - void onAddMapClickListener(OnMapClickListener listener); - - void onRemoveMapClickListener(OnMapClickListener listener); - - void onAddMapLongClickListener(OnMapLongClickListener listener); - - void onRemoveMapLongClickListener(OnMapLongClickListener listener); - - void onAddFlingListener(OnFlingListener listener); - - void onRemoveFlingListener(OnFlingListener listener); - - void onAddMoveListener(OnMoveListener listener); - - void onRemoveMoveListener(OnMoveListener listener); - - void onAddRotateListener(OnRotateListener listener); - - void onRemoveRotateListener(OnRotateListener listener); - - void onAddScaleListener(OnScaleListener listener); - - void onRemoveScaleListener(OnScaleListener listener); - - void onAddShoveListener(OnShoveListener listener); - - void onRemoveShoveListener(OnShoveListener listener); - - AndroidGesturesManager getGesturesManager(); - - void setGesturesManager(AndroidGesturesManager gesturesManager, boolean attachDefaultListeners, - boolean setDefaultMutuallyExclusives); - - void cancelAllVelocityAnimations(); - } - - /** - * Interface definition for a callback to be invoked when the user clicks on the map view. - * - * @see MapboxMap#addOnMapClickListener(OnMapClickListener) - */ - public interface OnMapClickListener { - /** - * Called when the user clicks on the map view. - * - * @param point The projected map coordinate the user clicked on. - * @return True if this click should be consumed and not passed further to other listeners registered afterwards, - * false otherwise. - */ - boolean onMapClick(@NonNull LatLng point); - } - - /** - * Interface definition for a callback to be invoked when the user long clicks on the map view. - * - * @see MapboxMap#addOnMapLongClickListener(OnMapLongClickListener) - */ - public interface OnMapLongClickListener { - /** - * Called when the user long clicks on the map view. - * - * @param point The projected map coordinate the user long clicked on. - * @return True if this click should be consumed and not passed further to other listeners registered afterwards, - * false otherwise. - */ - boolean onMapLongClick(@NonNull LatLng point); - } - - /** - * Interface definition for a callback to be invoked when the user clicks on a marker. - * - * @see MapboxMap#setOnMarkerClickListener(OnMarkerClickListener) - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public interface OnMarkerClickListener { - /** - * Called when the user clicks on a marker. - * - * @param marker The marker the user clicked on. - * @return If true the listener has consumed the event and the info window will not be shown. - */ - boolean onMarkerClick(@NonNull Marker marker); - } - - /** - * Interface definition for a callback to be invoked when the user clicks on a polygon. - * - * @see MapboxMap#setOnPolygonClickListener(OnPolygonClickListener) - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public interface OnPolygonClickListener { - /** - * Called when the user clicks on a polygon. - * - * @param polygon The polygon the user clicked on. - */ - void onPolygonClick(@NonNull Polygon polygon); - } - - /** - * Interface definition for a callback to be invoked when the user clicks on a polyline. - * - * @see MapboxMap#setOnPolylineClickListener(OnPolylineClickListener) - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public interface OnPolylineClickListener { - /** - * Called when the user clicks on a polyline. - * - * @param polyline The polyline the user clicked on. - */ - void onPolylineClick(@NonNull Polyline polyline); - } - - /** - * Interface definition for a callback to be invoked when the user clicks on an info window. - * - * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) - */ - public interface OnInfoWindowClickListener { - /** - * Called when the user clicks on an info window. - * - * @param marker The marker of the info window the user clicked on. - * @return If true the listener has consumed the event and the info window will not be closed. - */ - boolean onInfoWindowClick(@NonNull Marker marker); - } - - /** - * Interface definition for a callback to be invoked when the user long presses on a marker's info window. - * - * @see MapboxMap#setOnInfoWindowClickListener(OnInfoWindowClickListener) - */ - public interface OnInfoWindowLongClickListener { - - /** - * Called when the user makes a long-press gesture on the marker's info window. - * - * @param marker The marker were the info window is attached to - */ - void onInfoWindowLongClick(@NonNull Marker marker); - } - - /** - * Interface definition for a callback to be invoked when a marker's info window is closed. - * - * @see MapboxMap#setOnInfoWindowCloseListener(OnInfoWindowCloseListener) - */ - public interface OnInfoWindowCloseListener { - - /** - * Called when the marker's info window is closed. - * - * @param marker The marker of the info window that was closed. - */ - void onInfoWindowClose(@NonNull Marker marker); - } - - /** - * Interface definition for a callback to be invoked when an info window will be shown. - * - * @see MapboxMap#setInfoWindowAdapter(InfoWindowAdapter) - * @deprecated As of 7.0.0, - * use - * Mapbox Annotation Plugin instead - */ - @Deprecated - public interface InfoWindowAdapter { - /** - * Called when an info window will be shown as a result of a marker click. - * - * @param marker The marker the user clicked on. - * @return View to be shown as a info window. If null is returned the default - * info window will be shown. - */ - @Nullable - View getInfoWindow(@NonNull Marker marker); - } - - /** - * Interface definition for a callback to be invoked when a task is complete or cancelled. - */ - public interface CancelableCallback { - /** - * Invoked when a task is cancelled. - */ - void onCancel(); - - /** - * Invoked when a task is complete. - */ - void onFinish(); - } - - /** - * Interface definition for a callback to be invoked when the snapshot has been taken. - */ - public interface SnapshotReadyCallback { - /** - * Invoked when the snapshot has been taken. - * - * @param snapshot the snapshot bitmap - */ - void onSnapshotReady(@NonNull Bitmap snapshot); - } - - /** - * Internal use. - */ - public interface OnDeveloperAnimationListener { - - /** - * Notifies listener when a developer invoked animation is about to start. - */ - void onDeveloperAnimationStarted(); - } - - // - // Used for instrumentation testing - // - @NonNull - Transform getTransform() { - return transform; - } - - private void notifyDeveloperAnimationListeners() { - for (OnDeveloperAnimationListener listener : developerAnimationStartedListeners) { - listener.onDeveloperAnimationStarted(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java deleted file mode 100644 index 827756870..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMapOptions.java +++ /dev/null @@ -1,1283 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.annotation.ColorInt; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import android.support.v4.content.res.ResourcesCompat; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.Gravity; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.utils.BitmapUtils; -import com.mapbox.mapboxsdk.utils.FontUtils; - -import java.util.Arrays; - -/** - * Defines configuration MapboxMapMapOptions for a MapboxMap. These options can be used when adding a - * map to your application programmatically (as opposed to via XML). If you are using a MapFragment, - * you can pass these options in using the static factory method newInstance(MapboxMapOptions). - * If you are using a MapView, you can pass these options in using the constructor - * MapView(Context, MapboxMapOptions). If you add a map using XML, then you can apply these options - * using custom XML tags. - */ -public class MapboxMapOptions implements Parcelable { - - private static final int LIGHT_GRAY = 0xFFF0E9E1; // RGB(240, 233, 225)) - private static final float FOUR_DP = 4f; - private static final float NINETY_TWO_DP = 92f; - private static final int UNDEFINED_COLOR = -1; - - private CameraPosition cameraPosition; - - private boolean debugActive; - - private boolean compassEnabled = true; - private boolean fadeCompassFacingNorth = true; - private int compassGravity = Gravity.TOP | Gravity.END; - private int[] compassMargins; - private Drawable compassImage; - - private boolean logoEnabled = true; - private int logoGravity = Gravity.BOTTOM | Gravity.START; - private int[] logoMargins; - - @ColorInt - private int attributionTintColor = UNDEFINED_COLOR; - private boolean attributionEnabled = true; - private int attributionGravity = Gravity.BOTTOM | Gravity.START; - private int[] attributionMargins; - - private double minZoom = MapboxConstants.MINIMUM_ZOOM; - private double maxZoom = MapboxConstants.MAXIMUM_ZOOM; - - private boolean rotateGesturesEnabled = true; - private boolean scrollGesturesEnabled = true; - private boolean tiltGesturesEnabled = true; - private boolean zoomGesturesEnabled = true; - private boolean doubleTapGesturesEnabled = true; - private boolean quickZoomGesturesEnabled = true; - - private boolean prefetchesTiles = true; - private int prefetchZoomDelta = 4; - private boolean zMediaOverlay = false; - - private boolean localIdeographFontFamilyEnabled = true; - private String localIdeographFontFamily; - private String[] localIdeographFontFamilies; - - private String apiBaseUri; - - private boolean textureMode; - private boolean translucentTextureSurface; - - @ColorInt - private int foregroundLoadColor; - - private float pixelRatio; - - private boolean crossSourceCollisions = true; - - /** - * Creates a new MapboxMapOptions object. - * - * @deprecated Use {@link #createFromAttributes(Context, AttributeSet)} instead. - */ - @Deprecated - public MapboxMapOptions() { - } - - private MapboxMapOptions(Parcel in) { - cameraPosition = in.readParcelable(CameraPosition.class.getClassLoader()); - debugActive = in.readByte() != 0; - - compassEnabled = in.readByte() != 0; - compassGravity = in.readInt(); - compassMargins = in.createIntArray(); - fadeCompassFacingNorth = in.readByte() != 0; - - Bitmap compassBitmap = in.readParcelable(getClass().getClassLoader()); - if (compassBitmap != null) { - compassImage = new BitmapDrawable(compassBitmap); - } - - logoEnabled = in.readByte() != 0; - logoGravity = in.readInt(); - logoMargins = in.createIntArray(); - - attributionEnabled = in.readByte() != 0; - attributionGravity = in.readInt(); - attributionMargins = in.createIntArray(); - attributionTintColor = in.readInt(); - - minZoom = in.readDouble(); - maxZoom = in.readDouble(); - - rotateGesturesEnabled = in.readByte() != 0; - scrollGesturesEnabled = in.readByte() != 0; - tiltGesturesEnabled = in.readByte() != 0; - zoomGesturesEnabled = in.readByte() != 0; - doubleTapGesturesEnabled = in.readByte() != 0; - quickZoomGesturesEnabled = in.readByte() != 0; - - apiBaseUri = in.readString(); - textureMode = in.readByte() != 0; - translucentTextureSurface = in.readByte() != 0; - prefetchesTiles = in.readByte() != 0; - prefetchZoomDelta = in.readInt(); - zMediaOverlay = in.readByte() != 0; - localIdeographFontFamilyEnabled = in.readByte() != 0; - localIdeographFontFamily = in.readString(); - localIdeographFontFamilies = in.createStringArray(); - pixelRatio = in.readFloat(); - foregroundLoadColor = in.readInt(); - crossSourceCollisions = in.readByte() != 0; - } - - /** - * Creates a default MapboxMapsOptions from a given context. - * - * @param context Context related to a map view. - * @return the MapboxMapOptions created from attributes - */ - @NonNull - public static MapboxMapOptions createFromAttributes(@NonNull Context context) { - return createFromAttributes(context, null); - } - - /** - * Creates a MapboxMapsOptions from the attribute set. - * - * @param context Context related to a map view. - * @param attrs Attributeset containing configuration - * @return the MapboxMapOptions created from attributes - */ - @NonNull - public static MapboxMapOptions createFromAttributes(@NonNull Context context, @Nullable AttributeSet attrs) { - TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.mapbox_MapView, 0, 0); - return createFromAttributes(new MapboxMapOptions(), context, typedArray); - } - - @VisibleForTesting - static MapboxMapOptions createFromAttributes(@NonNull MapboxMapOptions mapboxMapOptions, - @NonNull Context context, - @Nullable TypedArray typedArray) { - float pxlRatio = context.getResources().getDisplayMetrics().density; - try { - mapboxMapOptions.camera(new CameraPosition.Builder(typedArray).build()); - - // deprecated - mapboxMapOptions.apiBaseUrl(typedArray.getString(R.styleable.mapbox_MapView_mapbox_apiBaseUrl)); - - String baseUri = typedArray.getString(R.styleable.mapbox_MapView_mapbox_apiBaseUri); - if (!TextUtils.isEmpty(baseUri)) { - // override deprecated property if a value of the new type was provided - mapboxMapOptions.apiBaseUri(baseUri); - } - - mapboxMapOptions.zoomGesturesEnabled( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiZoomGestures, true)); - mapboxMapOptions.scrollGesturesEnabled( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiScrollGestures, true)); - mapboxMapOptions.rotateGesturesEnabled( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiRotateGestures, true)); - mapboxMapOptions.tiltGesturesEnabled( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiTiltGestures, true)); - mapboxMapOptions.doubleTapGesturesEnabled( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiDoubleTapGestures, true)); - mapboxMapOptions.quickZoomGesturesEnabled( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiQuickZoomGestures, true)); - - mapboxMapOptions.maxZoomPreference(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoomMax, - MapboxConstants.MAXIMUM_ZOOM)); - mapboxMapOptions.minZoomPreference(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoomMin, - MapboxConstants.MINIMUM_ZOOM)); - - mapboxMapOptions.compassEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiCompass, true)); - mapboxMapOptions.compassGravity(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiCompassGravity, - Gravity.TOP | Gravity.END)); - mapboxMapOptions.compassMargins(new int[] { - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginLeft, - FOUR_DP * pxlRatio)), - ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginTop, - FOUR_DP * pxlRatio)), - ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginRight, - FOUR_DP * pxlRatio)), - ((int) typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiCompassMarginBottom, - FOUR_DP * pxlRatio))}); - mapboxMapOptions.compassFadesWhenFacingNorth(typedArray.getBoolean( - R.styleable.mapbox_MapView_mapbox_uiCompassFadeFacingNorth, true)); - Drawable compassDrawable = typedArray.getDrawable( - R.styleable.mapbox_MapView_mapbox_uiCompassDrawable); - if (compassDrawable == null) { - compassDrawable = ResourcesCompat.getDrawable(context.getResources(), R.drawable.mapbox_compass_icon, null); - } - mapboxMapOptions.compassImage(compassDrawable); - - mapboxMapOptions.logoEnabled(typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_uiLogo, true)); - mapboxMapOptions.logoGravity(typedArray.getInt(R.styleable.mapbox_MapView_mapbox_uiLogoGravity, - Gravity.BOTTOM | Gravity.START)); - mapboxMapOptions.logoMargins(new int[] { - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginLeft, - FOUR_DP * pxlRatio)), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginTop, - FOUR_DP * pxlRatio)), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginRight, - FOUR_DP * pxlRatio)), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiLogoMarginBottom, - FOUR_DP * pxlRatio))}); - - mapboxMapOptions.attributionTintColor(typedArray.getColor( - R.styleable.mapbox_MapView_mapbox_uiAttributionTintColor, UNDEFINED_COLOR)); - mapboxMapOptions.attributionEnabled(typedArray.getBoolean( - R.styleable.mapbox_MapView_mapbox_uiAttribution, true)); - mapboxMapOptions.attributionGravity(typedArray.getInt( - R.styleable.mapbox_MapView_mapbox_uiAttributionGravity, Gravity.BOTTOM | Gravity.START)); - mapboxMapOptions.attributionMargins(new int[] { - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginLeft, - NINETY_TWO_DP * pxlRatio)), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginTop, - FOUR_DP * pxlRatio)), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginRight, - FOUR_DP * pxlRatio)), - (int) (typedArray.getDimension(R.styleable.mapbox_MapView_mapbox_uiAttributionMarginBottom, - FOUR_DP * pxlRatio))}); - mapboxMapOptions.textureMode( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureMode, false)); - mapboxMapOptions.translucentTextureSurface( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_renderTextureTranslucentSurface, false)); - mapboxMapOptions.setPrefetchesTiles( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableTilePrefetch, true)); - mapboxMapOptions.setPrefetchZoomDelta( - typedArray.getInt(R.styleable.mapbox_MapView_mapbox_prefetchZoomDelta, 4)); - mapboxMapOptions.renderSurfaceOnTop( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_enableZMediaOverlay, false)); - - mapboxMapOptions.localIdeographFontFamilyEnabled = - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_localIdeographEnabled, true); - - int localIdeographFontFamiliesResId = - typedArray.getResourceId(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamilies, 0); - if (localIdeographFontFamiliesResId != 0) { - String[] localIdeographFontFamilies = - context.getResources().getStringArray(localIdeographFontFamiliesResId); - mapboxMapOptions.localIdeographFontFamily(localIdeographFontFamilies); - } else { - // did user provide xml font string? - String localIdeographFontFamily = - typedArray.getString(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamily); - if (localIdeographFontFamily == null) { - localIdeographFontFamily = MapboxConstants.DEFAULT_FONT; - } - mapboxMapOptions.localIdeographFontFamily(localIdeographFontFamily); - } - - mapboxMapOptions.pixelRatio( - typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_pixelRatio, 0)); - mapboxMapOptions.foregroundLoadColor( - typedArray.getInt(R.styleable.mapbox_MapView_mapbox_foregroundLoadColor, LIGHT_GRAY) - ); - mapboxMapOptions.crossSourceCollisions( - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_cross_source_collisions, true) - ); - } finally { - typedArray.recycle(); - } - return mapboxMapOptions; - } - - /** - * Specifies the URL used for API endpoint. - * - * @param apiBaseUrl The base of our API endpoint - * @return This - * @deprecated use {@link #apiBaseUri} instead - */ - @Deprecated - @NonNull - public MapboxMapOptions apiBaseUrl(String apiBaseUrl) { - this.apiBaseUri = apiBaseUrl; - return this; - } - - /** - * Specifies the URI used for API endpoint. - * - * @param apiBaseUri The base of our API endpoint - * @return This - */ - @NonNull - public MapboxMapOptions apiBaseUri(String apiBaseUri) { - this.apiBaseUri = apiBaseUri; - return this; - } - - /** - * Specifies a the initial camera position for the map view. - * - * @param cameraPosition Inital camera position - * @return This - */ - @NonNull - public MapboxMapOptions camera(CameraPosition cameraPosition) { - this.cameraPosition = cameraPosition; - return this; - } - - /** - * Specifies the used debug type for a map view. - * - * @param enabled True is debug is enabled - * @return This - */ - @NonNull - public MapboxMapOptions debugActive(boolean enabled) { - debugActive = enabled; - return this; - } - - /** - * Specifies the used minimum zoom level for a map view. - * - * @param minZoom Zoom level to be used - * @return This - */ - @NonNull - public MapboxMapOptions minZoomPreference(double minZoom) { - this.minZoom = minZoom; - return this; - } - - /** - * Specifies the used maximum zoom level for a map view. - * - * @param maxZoom Zoom level to be used - * @return This - */ - @NonNull - public MapboxMapOptions maxZoomPreference(double maxZoom) { - this.maxZoom = maxZoom; - return this; - } - - /** - * Specifies the visibility state of a mapbox_compass_icon for a map view. - * - * @param enabled True and mapbox_compass_icon is shown - * @return This - */ - @NonNull - public MapboxMapOptions compassEnabled(boolean enabled) { - compassEnabled = enabled; - return this; - } - - /** - * Specifies the gravity state of mapbox_compass_icon for a map view. - * - * @param gravity Android SDK Gravity. - * @return This - */ - @NonNull - public MapboxMapOptions compassGravity(int gravity) { - compassGravity = gravity; - return this; - } - - /** - * Specifies the margin state of mapbox_compass_icon for a map view - * - * @param margins 4 long array for LTRB margins - * @return This - */ - @NonNull - public MapboxMapOptions compassMargins(int[] margins) { - compassMargins = margins; - return this; - } - - /** - * Specifies if the mapbox_compass_icon fades to invisible when facing north. - *

- * By default this value is true. - *

- * - * @param compassFadeWhenFacingNorth true is mapbox_compass_icon fades to invisble - * @return This - */ - @NonNull - public MapboxMapOptions compassFadesWhenFacingNorth(boolean compassFadeWhenFacingNorth) { - this.fadeCompassFacingNorth = compassFadeWhenFacingNorth; - return this; - } - - /** - * Specifies the image of the CompassView. - *

- * By default this value is R.drawable.mapbox_compass_icon. - *

- * - * @param compass the drawable to show as image compass - * @return This - */ - @NonNull - public MapboxMapOptions compassImage(Drawable compass) { - this.compassImage = compass; - return this; - } - - /** - * Specifies the visibility state of a logo for a map view. - * - * @param enabled True and logo is shown - * @return This - */ - @NonNull - public MapboxMapOptions logoEnabled(boolean enabled) { - logoEnabled = enabled; - return this; - } - - /** - * Specifies the gravity state of logo for a map view. - * - * @param gravity Android SDK Gravity. - * @return This - */ - @NonNull - public MapboxMapOptions logoGravity(int gravity) { - logoGravity = gravity; - return this; - } - - /** - * Specifies the margin state of logo for a map view - * - * @param margins 4 long array for LTRB margins - * @return This - */ - @NonNull - public MapboxMapOptions logoMargins(int[] margins) { - logoMargins = margins; - return this; - } - - /** - * Specifies the visibility state of a attribution for a map view. - * - * @param enabled True and attribution is shown - * @return This - */ - @NonNull - public MapboxMapOptions attributionEnabled(boolean enabled) { - attributionEnabled = enabled; - return this; - } - - /** - * Specifies the gravity state of attribution for a map view. - * - * @param gravity Android SDK Gravity. - * @return This - */ - @NonNull - public MapboxMapOptions attributionGravity(int gravity) { - attributionGravity = gravity; - return this; - } - - /** - * Specifies the margin state of attribution for a map view - * - * @param margins 4 long array for LTRB margins - * @return This - */ - @NonNull - public MapboxMapOptions attributionMargins(int[] margins) { - attributionMargins = margins; - return this; - } - - /** - * Specifies the tint color of the attribution for a map view - * - * @param color integer resembling a color - * @return This - */ - @NonNull - public MapboxMapOptions attributionTintColor(@ColorInt int color) { - attributionTintColor = color; - return this; - } - - /** - * Specifies if the rotate gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - @NonNull - public MapboxMapOptions rotateGesturesEnabled(boolean enabled) { - rotateGesturesEnabled = enabled; - return this; - } - - /** - * Specifies if the scroll gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - @NonNull - public MapboxMapOptions scrollGesturesEnabled(boolean enabled) { - scrollGesturesEnabled = enabled; - return this; - } - - /** - * Specifies if the tilt gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - @NonNull - public MapboxMapOptions tiltGesturesEnabled(boolean enabled) { - tiltGesturesEnabled = enabled; - return this; - } - - /** - * Specifies if the zoom gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - @NonNull - public MapboxMapOptions zoomGesturesEnabled(boolean enabled) { - zoomGesturesEnabled = enabled; - return this; - } - - /** - * Specifies if the double tap gesture is enabled for a map view. - * - * @param enabled True and gesture will be enabled - * @return This - */ - @NonNull - public MapboxMapOptions doubleTapGesturesEnabled(boolean enabled) { - doubleTapGesturesEnabled = enabled; - return this; - } - - /** - * Specifies whether the user may zoom the map by tapping twice, holding and moving the pointer up and down. - * - * @param enabled True and gesture will be enabled - * @return This - */ - @NonNull - public MapboxMapOptions quickZoomGesturesEnabled(boolean enabled) { - quickZoomGesturesEnabled = enabled; - return this; - } - - /** - * Enable {@link android.view.TextureView} as rendered surface. - *

- * Since the 5.2.0 release we replaced our TextureView with an {@link android.opengl.GLSurfaceView} - * implementation. Enabling this option will use the {@link android.view.TextureView} instead. - * {@link android.view.TextureView} can be useful in situations where you need to animate, scale - * or transform the view. This comes at a siginficant performance penalty and should not be considered - * unless absolutely needed. - *

- * - * @param textureMode True to enable texture mode - * @return This - */ - @NonNull - public MapboxMapOptions textureMode(boolean textureMode) { - this.textureMode = textureMode; - return this; - } - - @NonNull - public MapboxMapOptions translucentTextureSurface(boolean translucentTextureSurface) { - this.translucentTextureSurface = translucentTextureSurface; - return this; - } - - /** - * Set the MapView foreground color that is used when the map surface is being created. - * - * @param loadColor the color to show during map creation - * @return This - */ - @NonNull - public MapboxMapOptions foregroundLoadColor(@ColorInt int loadColor) { - this.foregroundLoadColor = loadColor; - return this; - } - - /** - * Enable tile pre-fetching. Loads tiles at a lower zoom-level to pre-render - * a low resolution preview while more detailed tiles are loaded. - * Enabled by default - * - * @param enable true to enable - * @return This - * @deprecated Use {@link #setPrefetchZoomDelta(int)} instead. - */ - @Deprecated - @NonNull - public MapboxMapOptions setPrefetchesTiles(boolean enable) { - this.prefetchesTiles = enable; - return this; - } - - /** - * Set the tile pre-fetching zoom delta. Pre-fetching makes sure that a low-resolution - * tile at the (current_zoom_level - delta) is rendered as soon as possible at the - * expense of a little bandwidth. - * Note: This operation will override the MapboxMapOptions#setPrefetchesTiles(boolean) - * Setting zoom delta to 0 will disable pre-fetching. - * Default zoom delta is 4. - * - * @param delta zoom delta - * @return This - */ - @NonNull - public MapboxMapOptions setPrefetchZoomDelta(@IntRange(from = 0) int delta) { - this.prefetchZoomDelta = delta; - return this; - } - - /** - * Enable cross-source symbol collision detection, defaults to true. - *

- * If set to false, symbol layers will only run collision detection against - * other symbol layers that are part of the same source. - *

- * - * @param crossSourceCollisions true to enable, false to disable - * @return This - */ - @NonNull - public MapboxMapOptions crossSourceCollisions(boolean crossSourceCollisions) { - this.crossSourceCollisions = crossSourceCollisions; - return this; - } - - /** - * Enable local ideograph font family, defaults to true. - * - * @param enabled true to enable, false to disable - * @return This - */ - @NonNull - public MapboxMapOptions localIdeographFontFamilyEnabled(boolean enabled) { - this.localIdeographFontFamilyEnabled = enabled; - return this; - } - - /** - * Set the font family for generating glyphs locally for ideographs in the 'CJK Unified Ideographs' - * and 'Hangul Syllables' ranges. - *

- * The font family argument is passed to {@link android.graphics.Typeface#create(String, int)}. - * Default system fonts are defined in '/system/etc/fonts.xml' - * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}. - * - * @param fontFamily font family for local ideograph generation. - * @return This - */ - @NonNull - public MapboxMapOptions localIdeographFontFamily(String fontFamily) { - this.localIdeographFontFamily = FontUtils.extractValidFont(fontFamily); - return this; - } - - /** - * Set a font family from range of font families for generating glyphs locally for ideographs in the - * 'CJK Unified Ideographs' and 'Hangul Syllables' ranges. The first matching font - * will be selected. If no valid font found, it defaults to {@link MapboxConstants#DEFAULT_FONT}. - *

- * The font families are checked against the default system fonts defined in - * '/system/etc/fonts.xml' Default font for local ideograph font family is - * {@link MapboxConstants#DEFAULT_FONT}. - *

- * - * @param fontFamilies an array of font families for local ideograph generation. - * @return This - */ - @NonNull - public MapboxMapOptions localIdeographFontFamily(String... fontFamilies) { - this.localIdeographFontFamily = FontUtils.extractValidFont(fontFamilies); - return this; - } - - /** - * Set the custom pixel ratio configuration to override the default value from resources. - * This ratio will be used to initialise the map with. - * - * @param pixelRatio the custom pixel ratio of the map under construction - * @return This - */ - @NonNull - public MapboxMapOptions pixelRatio(float pixelRatio) { - this.pixelRatio = pixelRatio; - return this; - } - - /** - * Check whether tile pre-fetching is enabled. - * - * @return true if enabled - * @deprecated Use {@link #getPrefetchZoomDelta()} instead. - */ - @Deprecated - public boolean getPrefetchesTiles() { - return prefetchesTiles; - } - - /** - * Check current pre-fetching zoom delta. - * - * @return current zoom delta. - */ - @IntRange(from = 0) - public int getPrefetchZoomDelta() { - return prefetchZoomDelta; - } - - /** - * Check whether cross-source symbol collision detection is enabled. - * - * @return true if enabled - */ - public boolean getCrossSourceCollisions() { - return crossSourceCollisions; - } - - /** - * Set the flag to render the map surface on top of another surface. - * - * @param renderOnTop true if this map is shown on top of another one, false if bottom. - */ - public void renderSurfaceOnTop(boolean renderOnTop) { - this.zMediaOverlay = renderOnTop; - } - - /** - * Get the flag to render the map surface on top of another surface. - * - * @return true if this map is - */ - public boolean getRenderSurfaceOnTop() { - return zMediaOverlay; - } - - /** - * Get the current configured API endpoint base URL. - * - * @return Base URL to be used API endpoint. - * @deprecated use {@link #getApiBaseUri()} instead - */ - @Deprecated - public String getApiBaseUrl() { - return apiBaseUri; - } - - /** - * Get the current configured API endpoint base URI. - * - * @return Base URI to be used API endpoint. - */ - public String getApiBaseUri() { - return apiBaseUri; - } - - /** - * Get the current configured initial camera position for a map view. - * - * @return CameraPosition to be initially used. - */ - public CameraPosition getCamera() { - return cameraPosition; - } - - /** - * Get the current configured min zoom for a map view. - * - * @return Mininum zoom level to be used. - */ - public double getMinZoomPreference() { - return minZoom; - } - - /** - * Get the current configured maximum zoom for a map view. - * - * @return Maximum zoom to be used. - */ - public double getMaxZoomPreference() { - return maxZoom; - } - - /** - * Get the current configured visibility state for mapbox_compass_icon for a map view. - * - * @return Visibility state of the mapbox_compass_icon - */ - public boolean getCompassEnabled() { - return compassEnabled; - } - - /** - * Get the current configured gravity state for mapbox_compass_icon for a map view. - * - * @return Gravity state of the mapbox_compass_icon - */ - public int getCompassGravity() { - return compassGravity; - } - - /** - * Get the current configured margins for mapbox_compass_icon for a map view. - * - * @return Margins state of the mapbox_compass_icon - */ - public int[] getCompassMargins() { - return compassMargins; - } - - /** - * Get the current configured state for fading the mapbox_compass_icon when facing north. - * - * @return True if mapbox_compass_icon fades to invisible when facing north - */ - public boolean getCompassFadeFacingNorth() { - return fadeCompassFacingNorth; - } - - /** - * Get the current configured CompassView image. - * - * @return the drawable used as compass image - */ - public Drawable getCompassImage() { - return compassImage; - } - - /** - * Get the current configured visibility state for mapbox_compass_icon for a map view. - * - * @return Visibility state of the mapbox_compass_icon - */ - public boolean getLogoEnabled() { - return logoEnabled; - } - - /** - * Get the current configured gravity state for logo for a map view. - * - * @return Gravity state of the logo - */ - public int getLogoGravity() { - return logoGravity; - } - - /** - * Get the current configured margins for logo for a map view. - * - * @return Margins state of the logo - */ - public int[] getLogoMargins() { - return logoMargins; - } - - /** - * Get the current configured rotate gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getRotateGesturesEnabled() { - return rotateGesturesEnabled; - } - - /** - * Get the current configured scroll gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getScrollGesturesEnabled() { - return scrollGesturesEnabled; - } - - /** - * Get the current configured tilt gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getTiltGesturesEnabled() { - return tiltGesturesEnabled; - } - - /** - * Get the current configured zoom gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getZoomGesturesEnabled() { - return zoomGesturesEnabled; - } - - /** - * Get the current configured double tap gesture state for a map view. - * - * @return True indicates gesture is enabled - */ - public boolean getDoubleTapGesturesEnabled() { - return doubleTapGesturesEnabled; - } - - /** - * Get whether the user may zoom the map by tapping twice, holding and moving the pointer up and down. - * - * @return True indicates gesture is enabled - */ - public boolean getQuickZoomGesturesEnabled() { - return quickZoomGesturesEnabled; - } - - /** - * Get the current configured visibility state for attribution for a map view. - * - * @return Visibility state of the attribution - */ - public boolean getAttributionEnabled() { - return attributionEnabled; - } - - /** - * Get the current configured gravity state for attribution for a map view. - * - * @return Gravity state of the logo - */ - public int getAttributionGravity() { - return attributionGravity; - } - - /** - * Get the current configured margins for attribution for a map view. - * - * @return Margins state of the logo - */ - public int[] getAttributionMargins() { - return attributionMargins; - } - - /** - * Get the current configured tint color for attribution for a map view. - * - * @return the tint color - */ - @ColorInt - public int getAttributionTintColor() { - return attributionTintColor; - } - - /** - * Get the current configured debug state for a map view. - * - * @return True indicates debug is enabled. - */ - public boolean getDebugActive() { - return debugActive; - } - - /** - * Returns true if TextureView is being used the render view. - * - * @return True if TextureView is used. - */ - public boolean getTextureMode() { - return textureMode; - } - - /** - * Returns true if TextureView supports a translucent surface - * - * @return True if translucent surface is active - */ - public boolean getTranslucentTextureSurface() { - return translucentTextureSurface; - } - - /** - * Returns the current configured foreground color that is used during map creation. - * - * @return the load color - */ - @ColorInt - public int getForegroundLoadColor() { - return foregroundLoadColor; - } - - /** - * Returns the font-family for locally overriding generation of glyphs in the - * 'CJK Unified Ideographs' and 'Hangul Syllables' ranges. - * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}. - * Returns null if local ideograph font families are disabled. - * - * @return Local ideograph font family name. - */ - @Nullable - public String getLocalIdeographFontFamily() { - return localIdeographFontFamilyEnabled ? localIdeographFontFamily : null; - } - - /** - * Returns true if local ideograph font family is enabled, defaults to true. - * - * @return True if local ideograph font family is enabled - */ - public boolean isLocalIdeographFontFamilyEnabled() { - return localIdeographFontFamilyEnabled; - } - - /** - * Return the custom configured pixel ratio, returns 0 if not configured. - * - * @return the pixel ratio used by the map under construction - */ - public float getPixelRatio() { - return pixelRatio; - } - - public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { - public MapboxMapOptions createFromParcel(@NonNull Parcel in) { - return new MapboxMapOptions(in); - } - - public MapboxMapOptions[] newArray(int size) { - return new MapboxMapOptions[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeParcelable(cameraPosition, flags); - dest.writeByte((byte) (debugActive ? 1 : 0)); - - dest.writeByte((byte) (compassEnabled ? 1 : 0)); - dest.writeInt(compassGravity); - dest.writeIntArray(compassMargins); - dest.writeByte((byte) (fadeCompassFacingNorth ? 1 : 0)); - dest.writeParcelable(compassImage != null - ? BitmapUtils.getBitmapFromDrawable(compassImage) : null, flags); - - dest.writeByte((byte) (logoEnabled ? 1 : 0)); - dest.writeInt(logoGravity); - dest.writeIntArray(logoMargins); - - dest.writeByte((byte) (attributionEnabled ? 1 : 0)); - dest.writeInt(attributionGravity); - dest.writeIntArray(attributionMargins); - dest.writeInt(attributionTintColor); - - dest.writeDouble(minZoom); - dest.writeDouble(maxZoom); - - dest.writeByte((byte) (rotateGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (scrollGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (tiltGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (zoomGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (doubleTapGesturesEnabled ? 1 : 0)); - dest.writeByte((byte) (quickZoomGesturesEnabled ? 1 : 0)); - - dest.writeString(apiBaseUri); - dest.writeByte((byte) (textureMode ? 1 : 0)); - dest.writeByte((byte) (translucentTextureSurface ? 1 : 0)); - dest.writeByte((byte) (prefetchesTiles ? 1 : 0)); - dest.writeInt(prefetchZoomDelta); - dest.writeByte((byte) (zMediaOverlay ? 1 : 0)); - dest.writeByte((byte) (localIdeographFontFamilyEnabled ? 1 : 0)); - dest.writeString(localIdeographFontFamily); - dest.writeStringArray(localIdeographFontFamilies); - dest.writeFloat(pixelRatio); - dest.writeInt(foregroundLoadColor); - dest.writeByte((byte) (crossSourceCollisions ? 1 : 0)); - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - MapboxMapOptions options = (MapboxMapOptions) o; - - if (debugActive != options.debugActive) { - return false; - } - if (compassEnabled != options.compassEnabled) { - return false; - } - if (fadeCompassFacingNorth != options.fadeCompassFacingNorth) { - return false; - } - if (compassImage != null - ? !compassImage.equals(options.compassImage) - : options.compassImage != null) { - return false; - } - if (compassGravity != options.compassGravity) { - return false; - } - if (logoEnabled != options.logoEnabled) { - return false; - } - if (logoGravity != options.logoGravity) { - return false; - } - if (attributionTintColor != options.attributionTintColor) { - return false; - } - if (attributionEnabled != options.attributionEnabled) { - return false; - } - if (attributionGravity != options.attributionGravity) { - return false; - } - if (Double.compare(options.minZoom, minZoom) != 0) { - return false; - } - if (Double.compare(options.maxZoom, maxZoom) != 0) { - return false; - } - if (rotateGesturesEnabled != options.rotateGesturesEnabled) { - return false; - } - if (scrollGesturesEnabled != options.scrollGesturesEnabled) { - return false; - } - if (tiltGesturesEnabled != options.tiltGesturesEnabled) { - return false; - } - if (zoomGesturesEnabled != options.zoomGesturesEnabled) { - return false; - } - if (doubleTapGesturesEnabled != options.doubleTapGesturesEnabled) { - return false; - } - if (quickZoomGesturesEnabled != options.quickZoomGesturesEnabled) { - return false; - } - if (cameraPosition != null ? !cameraPosition.equals(options.cameraPosition) : options.cameraPosition != null) { - return false; - } - if (!Arrays.equals(compassMargins, options.compassMargins)) { - return false; - } - if (!Arrays.equals(logoMargins, options.logoMargins)) { - return false; - } - if (!Arrays.equals(attributionMargins, options.attributionMargins)) { - return false; - } - if (apiBaseUri != null ? !apiBaseUri.equals(options.apiBaseUri) : options.apiBaseUri != null) { - return false; - } - if (prefetchesTiles != options.prefetchesTiles) { - return false; - } - if (prefetchZoomDelta != options.prefetchZoomDelta) { - return false; - } - if (zMediaOverlay != options.zMediaOverlay) { - return false; - } - if (localIdeographFontFamilyEnabled != options.localIdeographFontFamilyEnabled) { - return false; - } - if (!localIdeographFontFamily.equals(options.localIdeographFontFamily)) { - return false; - } - if (!Arrays.equals(localIdeographFontFamilies, options.localIdeographFontFamilies)) { - return false; - } - - if (pixelRatio != options.pixelRatio) { - return false; - } - - if (crossSourceCollisions != options.crossSourceCollisions) { - return false; - } - - return false; - } - - @Override - public int hashCode() { - int result; - long temp; - result = cameraPosition != null ? cameraPosition.hashCode() : 0; - result = 31 * result + (debugActive ? 1 : 0); - result = 31 * result + (compassEnabled ? 1 : 0); - result = 31 * result + (fadeCompassFacingNorth ? 1 : 0); - result = 31 * result + compassGravity; - result = 31 * result + (compassImage != null ? compassImage.hashCode() : 0); - result = 31 * result + Arrays.hashCode(compassMargins); - result = 31 * result + (logoEnabled ? 1 : 0); - result = 31 * result + logoGravity; - result = 31 * result + Arrays.hashCode(logoMargins); - result = 31 * result + attributionTintColor; - result = 31 * result + (attributionEnabled ? 1 : 0); - result = 31 * result + attributionGravity; - result = 31 * result + Arrays.hashCode(attributionMargins); - temp = Double.doubleToLongBits(minZoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(maxZoom); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (rotateGesturesEnabled ? 1 : 0); - result = 31 * result + (scrollGesturesEnabled ? 1 : 0); - result = 31 * result + (tiltGesturesEnabled ? 1 : 0); - result = 31 * result + (zoomGesturesEnabled ? 1 : 0); - result = 31 * result + (doubleTapGesturesEnabled ? 1 : 0); - result = 31 * result + (quickZoomGesturesEnabled ? 1 : 0); - result = 31 * result + (apiBaseUri != null ? apiBaseUri.hashCode() : 0); - result = 31 * result + (textureMode ? 1 : 0); - result = 31 * result + (translucentTextureSurface ? 1 : 0); - result = 31 * result + (prefetchesTiles ? 1 : 0); - result = 31 * result + prefetchZoomDelta; - result = 31 * result + (zMediaOverlay ? 1 : 0); - result = 31 * result + (localIdeographFontFamilyEnabled ? 1 : 0); - result = 31 * result + (localIdeographFontFamily != null ? localIdeographFontFamily.hashCode() : 0); - result = 31 * result + Arrays.hashCode(localIdeographFontFamilies); - result = 31 * result + (int) pixelRatio; - result = 31 * result + (crossSourceCollisions ? 1 : 0); - return result; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java deleted file mode 100644 index b408d9f5c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MarkerContainer.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.RectF; -import android.support.annotation.NonNull; -import android.support.v4.util.LongSparseArray; - -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.Marker; - -import java.util.ArrayList; -import java.util.List; - -/** - * Encapsulates {@link Marker}'s functionality. - */ -class MarkerContainer implements Markers { - - private final NativeMap nativeMapView; - private final LongSparseArray annotations; - private final IconManager iconManager; - - MarkerContainer(NativeMap nativeMapView, LongSparseArray annotations, IconManager iconManager) { - this.nativeMapView = nativeMapView; - this.annotations = annotations; - this.iconManager = iconManager; - } - - @Override - public Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap) { - Marker marker = prepareMarker(markerOptions); - long id = nativeMapView != null ? nativeMapView.addMarker(marker) : 0; - marker.setMapboxMap(mapboxMap); - marker.setId(id); - annotations.put(id, marker); - return marker; - } - - @NonNull - @Override - public List addBy(@NonNull List markerOptionsList, @NonNull MapboxMap - mapboxMap) { - int count = markerOptionsList.size(); - List markers = new ArrayList<>(count); - if (nativeMapView != null && count > 0) { - BaseMarkerOptions markerOptions; - Marker marker; - for (int i = 0; i < count; i++) { - markerOptions = markerOptionsList.get(i); - marker = prepareMarker(markerOptions); - markers.add(marker); - } - - if (markers.size() > 0) { - long[] ids = nativeMapView.addMarkers(markers); - for (int i = 0; i < ids.length; i++) { - Marker createdMarker = markers.get(i); - createdMarker.setMapboxMap(mapboxMap); - createdMarker.setId(ids[i]); - annotations.put(ids[i], createdMarker); - } - } - } - return markers; - } - - @Override - public void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap) { - ensureIconLoaded(updatedMarker, mapboxMap); - nativeMapView.updateMarker(updatedMarker); - annotations.setValueAt(annotations.indexOfKey(updatedMarker.getId()), updatedMarker); - } - - @NonNull - @Override - public List obtainAll() { - List markers = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Marker) { - markers.add((Marker) annotation); - } - } - return markers; - } - - @NonNull - @Override - public List obtainAllIn(@NonNull RectF rectangle) { - RectF rect = nativeMapView.getDensityDependantRectangle(rectangle); - long[] ids = nativeMapView.queryPointAnnotations(rect); - List idsList = new ArrayList<>(ids.length); - for (long id : ids) { - idsList.add(id); - } - - List annotations = new ArrayList<>(ids.length); - List annotationList = obtainAnnotations(); - int count = annotationList.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotationList.get(i); - if (annotation instanceof com.mapbox.mapboxsdk.annotations.Marker && idsList.contains(annotation.getId())) { - annotations.add((com.mapbox.mapboxsdk.annotations.Marker) annotation); - } - } - - return new ArrayList<>(annotations); - } - - @Override - public void reload() { - iconManager.reloadIcons(); - int count = annotations.size(); - for (int i = 0; i < count; i++) { - Annotation annotation = annotations.get(i); - if (annotation instanceof Marker) { - Marker marker = (Marker) annotation; - nativeMapView.removeAnnotation(annotation.getId()); - long newId = nativeMapView.addMarker(marker); - marker.setId(newId); - } - } - } - - private Marker prepareMarker(BaseMarkerOptions markerOptions) { - Marker marker = markerOptions.getMarker(); - Icon icon = iconManager.loadIconForMarker(marker); - marker.setTopOffsetPixels(iconManager.getTopOffsetPixelsForIcon(icon)); - return marker; - } - - private void ensureIconLoaded(Marker marker, @NonNull MapboxMap mapboxMap) { - iconManager.ensureIconLoaded(marker, mapboxMap); - } - - @NonNull - private List obtainAnnotations() { - List annotations = new ArrayList<>(); - for (int i = 0; i < this.annotations.size(); i++) { - annotations.add(this.annotations.get(this.annotations.keyAt(i))); - } - return annotations; - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java deleted file mode 100644 index 62859d58a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Markers.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.RectF; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; -import com.mapbox.mapboxsdk.annotations.Marker; - -import java.util.List; - -/** - * Interface that defines convenient methods for working with a {@link Marker}'s collection. - */ -interface Markers { - Marker addBy(@NonNull BaseMarkerOptions markerOptions, @NonNull MapboxMap mapboxMap); - - List addBy(@NonNull List markerOptionsList, @NonNull MapboxMap mapboxMap); - - void update(@NonNull Marker updatedMarker, @NonNull MapboxMap mapboxMap); - - List obtainAll(); - - @NonNull - List obtainAllIn(@NonNull RectF rectangle); - - void reload(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java deleted file mode 100644 index c805dcddb..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java +++ /dev/null @@ -1,274 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.graphics.RectF; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Geometry; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.geometry.ProjectedMeters; -import com.mapbox.mapboxsdk.style.expressions.Expression; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.TransitionOptions; -import com.mapbox.mapboxsdk.style.light.Light; -import com.mapbox.mapboxsdk.style.sources.Source; - -import java.util.List; - -interface NativeMap { - - // - // Lifecycle API - // - - void resizeView(int width, int height); - - void onLowMemory(); - - void destroy(); - - boolean isDestroyed(); - - // - // Camera API - // - - void jumpTo(@NonNull LatLng center, double zoom, double pitch, double bearing, double[] padding); - - void easeTo(@NonNull LatLng center, double zoom, double bearing, double pitch, double[] padding, long duration, - boolean easingInterpolator); - - void flyTo(@NonNull LatLng center, double zoom, double bearing, double pitch, double[] padding, long duration); - - void moveBy(double deltaX, double deltaY, long duration); - - @NonNull - CameraPosition getCameraPosition(); - - CameraPosition getCameraForLatLngBounds(@NonNull LatLngBounds bounds, int[] padding, double bearing, double pitch); - - CameraPosition getCameraForGeometry(@NonNull Geometry geometry, int[] padding, double bearing, double pitch); - - void resetPosition(); - - void setLatLng(@NonNull LatLng latLng, long duration); - - LatLng getLatLng(); - - void setLatLngBounds(@Nullable LatLngBounds latLngBounds); - - void setVisibleCoordinateBounds(@NonNull LatLng[] coordinates, @NonNull RectF padding, - double direction, long duration); - - void setPitch(double pitch, long duration); - - double getPitch(); - - void setZoom(double zoom, @NonNull PointF focalPoint, long duration); - - double getZoom(); - - void setMinZoom(double zoom); - - double getMinZoom(); - - void setMaxZoom(double zoom); - - double getMaxZoom(); - - void resetZoom(); - - void rotateBy(double sx, double sy, double ex, double ey, long duration); - - void setBearing(double degrees, long duration); - - void setBearing(double degrees, double fx, double fy, long duration); - - double getBearing(); - - void resetNorth(); - - void cancelTransitions(); - - // - // Style API - // - - void setStyleUri(String url); - - @NonNull - String getStyleUri(); - - void setStyleJson(String newStyleJson); - - @NonNull - String getStyleJson(); - - boolean isFullyLoaded(); - - void addLayer(@NonNull Layer layer); - - void addLayerBelow(@NonNull Layer layer, @NonNull String below); - - void addLayerAbove(@NonNull Layer layer, @NonNull String above); - - void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index); - - @NonNull - List getLayers(); - - Layer getLayer(String layerId); - - boolean removeLayer(@NonNull String layerId); - - boolean removeLayer(@NonNull Layer layer); - - boolean removeLayerAt(@IntRange(from = 0) int index); - - void addSource(@NonNull Source source); - - @NonNull - List getSources(); - - Source getSource(@NonNull String sourceId); - - boolean removeSource(@NonNull String sourceId); - - boolean removeSource(@NonNull Source source); - - void setTransitionOptions(@NonNull TransitionOptions transitionOptions); - - @NonNull - TransitionOptions getTransitionOptions(); - - void addImages(Image[] images); - - Bitmap getImage(String name); - - void removeImage(String name); - - Light getLight(); - - // - // Content padding API - // - - void setContentPadding(double[] padding); - - double[] getContentPadding(); - - // - // Query API - // - - @NonNull - List queryRenderedFeatures(@NonNull PointF coordinates, - @Nullable String[] layerIds, - @Nullable Expression filter); - - @NonNull - List queryRenderedFeatures(@NonNull RectF coordinates, - @Nullable String[] layerIds, - @Nullable Expression filter); - - // - // Projection API - // - - double getMetersPerPixelAtLatitude(double lat); - - ProjectedMeters projectedMetersForLatLng(@NonNull LatLng latLng); - - LatLng latLngForProjectedMeters(@NonNull ProjectedMeters projectedMeters); - - @NonNull - PointF pixelForLatLng(@NonNull LatLng latLng); - - LatLng latLngForPixel(@NonNull PointF pixel); - - // - // Utils API - // - - void setOnFpsChangedListener(@NonNull MapboxMap.OnFpsChangedListener listener); - - void setDebug(boolean debug); - - boolean getDebug(); - - void cycleDebugOptions(); - - void setReachability(boolean status); - - void setApiBaseUrl(String baseUrl); - - void setPrefetchTiles(boolean enable); - - boolean getPrefetchTiles(); - - void setPrefetchZoomDelta(@IntRange(from = 0) int delta); - - @IntRange(from = 0) - int getPrefetchZoomDelta(); - - void setGestureInProgress(boolean inProgress); - - float getPixelRatio(); - - // - // Deprecated Annotations API - // - - long addMarker(Marker marker); - - @NonNull - long[] addMarkers(@NonNull List markers); - - long addPolyline(Polyline polyline); - - @NonNull - long[] addPolylines(@NonNull List polylines); - - long addPolygon(Polygon polygon); - - @NonNull - long[] addPolygons(@NonNull List polygons); - - void updateMarker(@NonNull Marker marker); - - void updatePolygon(@NonNull Polygon polygon); - - void updatePolyline(@NonNull Polyline polyline); - - void removeAnnotation(long id); - - void removeAnnotations(long[] ids); - - double getTopOffsetPixelsForAnnotationSymbol(String symbolName); - - void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels); - - void removeAnnotationIcon(String symbol); - - @NonNull - long[] queryPointAnnotations(RectF rectF); - - @NonNull - long[] queryShapeAnnotations(RectF rectF); - - @NonNull - RectF getDensityDependantRectangle(RectF rectangle); - - long getNativePtr(); - - void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback); -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java deleted file mode 100755 index 75ca5edfc..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ /dev/null @@ -1,1512 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.PointF; -import android.graphics.RectF; -import android.os.Handler; -import android.support.annotation.IntRange; -import android.support.annotation.Keep; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.text.TextUtils; - -import com.mapbox.geojson.Feature; -import com.mapbox.geojson.Geometry; -import com.mapbox.mapboxsdk.LibraryLoader; -import com.mapbox.mapboxsdk.MapStrictMode; -import com.mapbox.mapboxsdk.annotations.Icon; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.exceptions.CalledFromWorkerThreadException; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.geometry.ProjectedMeters; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.maps.renderer.MapRenderer; -import com.mapbox.mapboxsdk.storage.FileSource; -import com.mapbox.mapboxsdk.style.expressions.Expression; -import com.mapbox.mapboxsdk.style.layers.CannotAddLayerException; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.TransitionOptions; -import com.mapbox.mapboxsdk.style.light.Light; -import com.mapbox.mapboxsdk.style.sources.CannotAddSourceException; -import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.mapboxsdk.utils.BitmapUtils; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -// Class that wraps the native methods for convenience -final class NativeMapView implements NativeMap { - - private static final String TAG = "Mbgl-NativeMapView"; - - // Hold a reference to prevent it from being GC'd as long as it's used on the native side - private final FileSource fileSource; - - // Used to schedule work on the MapRenderer Thread - private final MapRenderer mapRenderer; - - // Used to validate if methods are called from the correct thread - @NonNull - private final Thread thread; - - // Used for view callbacks - @Nullable - private ViewCallback viewCallback; - - // Used for map change callbacks - @Nullable - private StateCallback stateCallback; - - // Device density - private final float pixelRatio; - - // Flag to indicate destroy was called - private boolean destroyed = false; - - // Cached to enable lazily set padding. - // Whenever an animation is schedule, this value is cleared and the source of truth becomes the core transform state. - private double[] edgeInsets; - - // Holds the pointer to JNI NativeMapView - @Keep - private long nativePtr = 0; - - // Listener invoked to return a bitmap of the map - private MapboxMap.SnapshotReadyCallback snapshotReadyCallback; - - static { - LibraryLoader.load(); - } - - // - // Constructors - // - - public NativeMapView(@NonNull final Context context, final boolean crossSourceCollisions, - final ViewCallback viewCallback, final StateCallback stateCallback, - final MapRenderer mapRenderer) { - this(context, context.getResources().getDisplayMetrics().density, crossSourceCollisions, viewCallback, - stateCallback, mapRenderer); - } - - public NativeMapView(final Context context, final float pixelRatio, final boolean crossSourceCollisions, - final ViewCallback viewCallback, final StateCallback stateCallback, - final MapRenderer mapRenderer) { - this.mapRenderer = mapRenderer; - this.viewCallback = viewCallback; - this.fileSource = FileSource.getInstance(context); - this.pixelRatio = pixelRatio; - this.thread = Thread.currentThread(); - this.stateCallback = stateCallback; - nativeInitialize(this, fileSource, mapRenderer, pixelRatio, crossSourceCollisions); - } - - // - // Methods - // - - private boolean checkState(String callingMethod) { - // validate if invocation has occurred on the main thread - if (thread != Thread.currentThread()) { - throw new CalledFromWorkerThreadException( - String.format( - "Map interactions should happen on the UI thread. Method invoked from wrong thread is %s.", - callingMethod) - ); - } - - // validate if map has already been destroyed - if (destroyed && !TextUtils.isEmpty(callingMethod)) { - String message = String.format( - "You're calling `%s` after the `MapView` was destroyed, were you invoking it after `onDestroy()`?", - callingMethod); - Logger.e(TAG, message); - - MapStrictMode.strictModeViolation(message); - } - return destroyed; - } - - @Override - public void destroy() { - destroyed = true; - viewCallback = null; - nativeDestroy(); - } - - @Override - public void resizeView(int width, int height) { - if (checkState("resizeView")) { - return; - } - width = (int) Math.ceil(width / pixelRatio); - height = (int) Math.ceil(height / pixelRatio); - - if (width < 0) { - throw new IllegalArgumentException("width cannot be negative."); - } - - if (height < 0) { - throw new IllegalArgumentException("height cannot be negative."); - } - - if (width > 65535) { - // we have seen edge cases where devices return incorrect values #6111 - Logger.e(TAG, String.format("Device returned an out of range width size, " - + "capping value at 65535 instead of %s", width) - ); - width = 65535; - } - - if (height > 65535) { - // we have seen edge cases where devices return incorrect values #6111 - Logger.e(TAG, String.format("Device returned an out of range height size, " - + "capping value at 65535 instead of %s", height) - ); - height = 65535; - } - - nativeResizeView(width, height); - } - - @Override - public void setStyleUri(String url) { - if (checkState("setStyleUri")) { - return; - } - nativeSetStyleUrl(url); - } - - @Override - @NonNull - public String getStyleUri() { - if (checkState("getStyleUri")) { - return ""; - } - return nativeGetStyleUrl(); - } - - @Override - public void setStyleJson(String newStyleJson) { - if (checkState("setStyleJson")) { - return; - } - nativeSetStyleJson(newStyleJson); - } - - @Override - @NonNull - public String getStyleJson() { - if (checkState("getStyleJson")) { - return ""; - } - return nativeGetStyleJson(); - } - - @Override - public void setLatLngBounds(LatLngBounds latLngBounds) { - if (checkState("setLatLngBounds")) { - return; - } - nativeSetLatLngBounds(latLngBounds); - } - - @Override - public void cancelTransitions() { - if (checkState("cancelTransitions")) { - return; - } - nativeCancelTransitions(); - } - - @Override - public void setGestureInProgress(boolean inProgress) { - if (checkState("setGestureInProgress")) { - return; - } - nativeSetGestureInProgress(inProgress); - } - - @Override - public void moveBy(double dx, double dy, long duration) { - if (checkState("moveBy")) { - return; - } - nativeMoveBy(dx / pixelRatio, dy / pixelRatio, duration); - } - - @Override - public void setLatLng(@NonNull LatLng latLng, long duration) { - if (checkState("setLatLng")) { - return; - } - nativeSetLatLng(latLng.getLatitude(), latLng.getLongitude(), - getAnimationPaddingAndClearCachedInsets(null), duration); - } - - @Override - public LatLng getLatLng() { - if (checkState("")) { - return new LatLng(); - } - return nativeGetLatLng(); - } - - @Override - public CameraPosition getCameraForLatLngBounds(LatLngBounds bounds, int[] padding, double bearing, double tilt) { - if (checkState("getCameraForLatLngBounds")) { - return null; - } - return nativeGetCameraForLatLngBounds( - bounds, - padding[1] / pixelRatio, - padding[0] / pixelRatio, - padding[3] / pixelRatio, - padding[2] / pixelRatio, - bearing, - tilt - ); - } - - @Override - public CameraPosition getCameraForGeometry(Geometry geometry, int[] padding, double bearing, double tilt) { - if (checkState("getCameraForGeometry")) { - return null; - } - return nativeGetCameraForGeometry( - geometry, - padding[1] / pixelRatio, - padding[0] / pixelRatio, - padding[3] / pixelRatio, - padding[2] / pixelRatio, - bearing, - tilt - ); - } - - @Override - public void resetPosition() { - if (checkState("resetPosition")) { - return; - } - nativeResetPosition(); - } - - @Override - public double getPitch() { - if (checkState("getPitch")) { - return 0; - } - return nativeGetPitch(); - } - - @Override - public void setPitch(double pitch, long duration) { - if (checkState("setPitch")) { - return; - } - nativeSetPitch(pitch, duration); - } - - @Override - public void setZoom(double zoom, @NonNull PointF focalPoint, long duration) { - if (checkState("setZoom")) { - return; - } - nativeSetZoom(zoom, focalPoint.x / pixelRatio, focalPoint.y / pixelRatio, duration); - } - - @Override - public double getZoom() { - if (checkState("getZoom")) { - return 0; - } - return nativeGetZoom(); - } - - @Override - public void resetZoom() { - if (checkState("resetZoom")) { - return; - } - nativeResetZoom(); - } - - @Override - public void setMinZoom(double zoom) { - if (checkState("setMinZoom")) { - return; - } - nativeSetMinZoom(zoom); - } - - @Override - public double getMinZoom() { - if (checkState("getMinZoom")) { - return 0; - } - return nativeGetMinZoom(); - } - - @Override - public void setMaxZoom(double zoom) { - if (checkState("setMaxZoom")) { - return; - } - nativeSetMaxZoom(zoom); - } - - @Override - public double getMaxZoom() { - if (checkState("getMaxZoom")) { - return 0; - } - return nativeGetMaxZoom(); - } - - @Override - public void rotateBy(double sx, double sy, double ex, double ey, - long duration) { - if (checkState("rotateBy")) { - return; - } - nativeRotateBy(sx / pixelRatio, sy / pixelRatio, ex, ey, duration); - } - - @Override - public void setContentPadding(double[] padding) { - if (checkState("setContentPadding")) { - return; - } - this.edgeInsets = padding; - } - - @Override - public double[] getContentPadding() { - if (checkState("getContentPadding")) { - return new double[] {0, 0, 0, 0}; - } - // if cached insets are not applied yet, return them, otherwise, get the padding from the camera - return edgeInsets != null ? edgeInsets : getCameraPosition().padding; - } - - @Override - public void setBearing(double degrees, long duration) { - if (checkState("setBearing")) { - return; - } - nativeSetBearing(degrees, duration); - } - - @Override - public void setBearing(double degrees, double fx, double fy, long duration) { - if (checkState("setBearing")) { - return; - } - nativeSetBearingXY(degrees, fx / pixelRatio, fy / pixelRatio, duration); - } - - @Override - public double getBearing() { - if (checkState("getBearing")) { - return 0; - } - return nativeGetBearing(); - } - - @Override - public void resetNorth() { - if (checkState("resetNorth")) { - return; - } - nativeResetNorth(); - } - - @Override - public long addMarker(Marker marker) { - if (checkState("addMarker")) { - return 0; - } - Marker[] markers = {marker}; - return nativeAddMarkers(markers)[0]; - } - - @Override - @NonNull - public long[] addMarkers(@NonNull List markers) { - if (checkState("addMarkers")) { - return new long[] {}; - } - return nativeAddMarkers(markers.toArray(new Marker[markers.size()])); - } - - @Override - public long addPolyline(Polyline polyline) { - if (checkState("addPolyline")) { - return 0; - } - Polyline[] polylines = {polyline}; - return nativeAddPolylines(polylines)[0]; - } - - @Override - @NonNull - public long[] addPolylines(@NonNull List polylines) { - if (checkState("addPolylines")) { - return new long[] {}; - } - return nativeAddPolylines(polylines.toArray(new Polyline[polylines.size()])); - } - - @Override - public long addPolygon(Polygon polygon) { - if (checkState("addPolygon")) { - return 0; - } - Polygon[] polygons = {polygon}; - return nativeAddPolygons(polygons)[0]; - } - - @Override - @NonNull - public long[] addPolygons(@NonNull List polygons) { - if (checkState("addPolygons")) { - return new long[] {}; - } - return nativeAddPolygons(polygons.toArray(new Polygon[polygons.size()])); - } - - @Override - public void updateMarker(@NonNull Marker marker) { - if (checkState("updateMarker")) { - return; - } - LatLng position = marker.getPosition(); - Icon icon = marker.getIcon(); - nativeUpdateMarker(marker.getId(), position.getLatitude(), position.getLongitude(), icon.getId()); - } - - @Override - public void updatePolygon(@NonNull Polygon polygon) { - if (checkState("updatePolygon")) { - return; - } - nativeUpdatePolygon(polygon.getId(), polygon); - } - - @Override - public void updatePolyline(@NonNull Polyline polyline) { - if (checkState("updatePolyline")) { - return; - } - nativeUpdatePolyline(polyline.getId(), polyline); - } - - @Override - public void removeAnnotation(long id) { - if (checkState("removeAnnotation")) { - return; - } - long[] ids = {id}; - removeAnnotations(ids); - } - - @Override - public void removeAnnotations(long[] ids) { - if (checkState("removeAnnotations")) { - return; - } - nativeRemoveAnnotations(ids); - } - - @Override - @NonNull - public long[] queryPointAnnotations(RectF rect) { - if (checkState("queryPointAnnotations")) { - return new long[] {}; - } - return nativeQueryPointAnnotations(rect); - } - - @Override - @NonNull - public long[] queryShapeAnnotations(RectF rectF) { - if (checkState("queryShapeAnnotations")) { - return new long[] {}; - } - return nativeQueryShapeAnnotations(rectF); - } - - @Override - public void addAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels) { - if (checkState("addAnnotationIcon")) { - return; - } - nativeAddAnnotationIcon(symbol, width, height, scale, pixels); - } - - @Override - public void removeAnnotationIcon(String symbol) { - if (checkState("removeAnnotationIcon")) { - return; - } - nativeRemoveAnnotationIcon(symbol); - } - - @Override - public void setVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, double direction, long duration) { - if (checkState("setVisibleCoordinateBounds")) { - return; - } - nativeSetVisibleCoordinateBounds(coordinates, padding, direction, duration); - } - - @Override - public void onLowMemory() { - if (checkState("onLowMemory")) { - return; - } - nativeOnLowMemory(); - } - - @Override - public void setDebug(boolean debug) { - if (checkState("setDebug")) { - return; - } - nativeSetDebug(debug); - } - - @Override - public void cycleDebugOptions() { - if (checkState("cycleDebugOptions")) { - return; - } - nativeCycleDebugOptions(); - } - - @Override - public boolean getDebug() { - if (checkState("getDebug")) { - return false; - } - return nativeGetDebug(); - } - - @Override - public boolean isFullyLoaded() { - if (checkState("isFullyLoaded")) { - return false; - } - return nativeIsFullyLoaded(); - } - - @Override - public void setReachability(boolean status) { - if (checkState("setReachability")) { - return; - } - nativeSetReachability(status); - } - - @Override - public double getMetersPerPixelAtLatitude(double lat) { - if (checkState("getMetersPerPixelAtLatitude")) { - return 0; - } - return nativeGetMetersPerPixelAtLatitude(lat, getZoom()); - } - - @Override - public ProjectedMeters projectedMetersForLatLng(@NonNull LatLng latLng) { - if (checkState("projectedMetersForLatLng")) { - return null; - } - return nativeProjectedMetersForLatLng(latLng.getLatitude(), latLng.getLongitude()); - } - - @Override - public LatLng latLngForProjectedMeters(@NonNull ProjectedMeters projectedMeters) { - if (checkState("latLngForProjectedMeters")) { - return new LatLng(); - } - return nativeLatLngForProjectedMeters(projectedMeters.getNorthing(), - projectedMeters.getEasting()); - } - - @Override - @NonNull - public PointF pixelForLatLng(@NonNull LatLng latLng) { - if (checkState("pixelForLatLng")) { - return new PointF(); - } - PointF pointF = nativePixelForLatLng(latLng.getLatitude(), latLng.getLongitude()); - pointF.set(pointF.x * pixelRatio, pointF.y * pixelRatio); - return pointF; - } - - @Override - public LatLng latLngForPixel(@NonNull PointF pixel) { - if (checkState("latLngForPixel")) { - return new LatLng(); - } - return nativeLatLngForPixel(pixel.x / pixelRatio, pixel.y / pixelRatio); - } - - @Override - public double getTopOffsetPixelsForAnnotationSymbol(String symbolName) { - if (checkState("getTopOffsetPixelsForAnnotationSymbol")) { - return 0; - } - return nativeGetTopOffsetPixelsForAnnotationSymbol(symbolName); - } - - @Override - public void jumpTo(@NonNull LatLng center, double zoom, double pitch, double angle, double[] padding) { - if (checkState("jumpTo")) { - return; - } - nativeJumpTo(angle, center.getLatitude(), center.getLongitude(), pitch, zoom, - getAnimationPaddingAndClearCachedInsets(padding)); - } - - @Override - public void easeTo(@NonNull LatLng center, double zoom, double angle, double pitch, double[] padding, long duration, - boolean easingInterpolator) { - if (checkState("easeTo")) { - return; - } - nativeEaseTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, - getAnimationPaddingAndClearCachedInsets(padding), easingInterpolator); - } - - @Override - public void flyTo(@NonNull LatLng center, double zoom, double angle, double pitch, double[] padding, long duration) { - if (checkState("flyTo")) { - return; - } - nativeFlyTo(angle, center.getLatitude(), center.getLongitude(), duration, pitch, zoom, - getAnimationPaddingAndClearCachedInsets(padding)); - } - - @Override - @NonNull - public CameraPosition getCameraPosition() { - if (checkState("getCameraValues")) { - return new CameraPosition.Builder().build(); - } - if (edgeInsets != null) { - return new CameraPosition.Builder(nativeGetCameraPosition()).padding(edgeInsets).build(); - } else { - return nativeGetCameraPosition(); - } - } - - @Override - public void setPrefetchTiles(boolean enable) { - if (checkState("setPrefetchTiles")) { - return; - } - nativeSetPrefetchTiles(enable); - } - - @Override - public boolean getPrefetchTiles() { - if (checkState("getPrefetchTiles")) { - return false; - } - return nativeGetPrefetchTiles(); - } - - @Override - public void setPrefetchZoomDelta(@IntRange(from = 0) int delta) { - if (checkState("nativeSetPrefetchZoomDelta")) { - return; - } - nativeSetPrefetchZoomDelta(delta); - } - - @Override - @IntRange(from = 0) - public int getPrefetchZoomDelta() { - if (checkState("nativeGetPrefetchZoomDelta")) { - return 0; - } - return nativeGetPrefetchZoomDelta(); - } - - // Runtime style Api - - @Override - public void setTransitionOptions(@NonNull TransitionOptions transitionOptions) { - nativeSetTransitionOptions(transitionOptions); - } - - @NonNull - @Override - public TransitionOptions getTransitionOptions() { - return nativeGetTransitionOptions(); - } - - @Override - @NonNull - public List getLayers() { - if (checkState("getLayers")) { - return new ArrayList<>(); - } - return Arrays.asList(nativeGetLayers()); - } - - @Override - public Layer getLayer(String layerId) { - if (checkState("getLayer")) { - return null; - } - return nativeGetLayer(layerId); - } - - @Override - public void addLayer(@NonNull Layer layer) { - if (checkState("addLayer")) { - return; - } - nativeAddLayer(layer.getNativePtr(), null); - } - - @Override - public void addLayerBelow(@NonNull Layer layer, @NonNull String below) { - if (checkState("addLayerBelow")) { - return; - } - nativeAddLayer(layer.getNativePtr(), below); - } - - @Override - public void addLayerAbove(@NonNull Layer layer, @NonNull String above) { - if (checkState("addLayerAbove")) { - return; - } - nativeAddLayerAbove(layer.getNativePtr(), above); - } - - @Override - public void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index) { - if (checkState("addLayerAt")) { - return; - } - nativeAddLayerAt(layer.getNativePtr(), index); - } - - @Override - public boolean removeLayer(@NonNull String layerId) { - if (checkState("removeLayer")) { - return false; - } - - Layer layer = getLayer(layerId); - if (layer != null) { - return removeLayer(layer); - } - return false; - } - - - @Override - public boolean removeLayer(@NonNull Layer layer) { - if (checkState("removeLayer")) { - return false; - } - return nativeRemoveLayer(layer.getNativePtr()); - } - - @Override - public boolean removeLayerAt(@IntRange(from = 0) int index) { - if (checkState("removeLayerAt")) { - return false; - } - return nativeRemoveLayerAt(index); - } - - @Override - @NonNull - public List getSources() { - if (checkState("getSources")) { - return new ArrayList<>(); - } - return Arrays.asList(nativeGetSources()); - } - - @Override - public Source getSource(@NonNull String sourceId) { - if (checkState("getSource")) { - return null; - } - return nativeGetSource(sourceId); - } - - @Override - public void addSource(@NonNull Source source) { - if (checkState("addSource")) { - return; - } - nativeAddSource(source, source.getNativePtr()); - } - - @Override - public boolean removeSource(@NonNull String sourceId) { - if (checkState("removeSource")) { - return false; - } - Source source = getSource(sourceId); - if (source != null) { - return removeSource(source); - } - return false; - } - - @Override - public boolean removeSource(@NonNull Source source) { - if (checkState("removeSource")) { - return false; - } - return nativeRemoveSource(source, source.getNativePtr()); - } - - @Override - public void addImages(@NonNull Image[] images) { - if (checkState("addImages")) { - return; - } - nativeAddImages(images); - } - - @Override - public void removeImage(String name) { - if (checkState("removeImage")) { - return; - } - nativeRemoveImage(name); - } - - @Override - public Bitmap getImage(String name) { - if (checkState("getImage")) { - return null; - } - return nativeGetImage(name); - } - - // Feature querying - - @Override - @NonNull - public List queryRenderedFeatures(@NonNull PointF coordinates, - @Nullable String[] layerIds, - @Nullable Expression filter) { - if (checkState("queryRenderedFeatures")) { - return new ArrayList<>(); - } - Feature[] features = nativeQueryRenderedFeaturesForPoint(coordinates.x / pixelRatio, - coordinates.y / pixelRatio, layerIds, filter != null ? filter.toArray() : null); - return features != null ? Arrays.asList(features) : new ArrayList(); - } - - @Override - @NonNull - public List queryRenderedFeatures(@NonNull RectF coordinates, - @Nullable String[] layerIds, - @Nullable Expression filter) { - if (checkState("queryRenderedFeatures")) { - return new ArrayList<>(); - } - Feature[] features = nativeQueryRenderedFeaturesForBox( - coordinates.left / pixelRatio, - coordinates.top / pixelRatio, - coordinates.right / pixelRatio, - coordinates.bottom / pixelRatio, - layerIds, - filter != null ? filter.toArray() : null); - return features != null ? Arrays.asList(features) : new ArrayList(); - } - - @Override - public void setApiBaseUrl(String baseUrl) { - if (checkState("setApiBaseUrl")) { - return; - } - fileSource.setApiBaseUrl(baseUrl); - } - - @Override - public Light getLight() { - if (checkState("getLight")) { - return null; - } - return nativeGetLight(); - } - - @Override - public float getPixelRatio() { - return pixelRatio; - } - - @NonNull - @Override - public RectF getDensityDependantRectangle(final RectF rectangle) { - return new RectF( - rectangle.left / pixelRatio, - rectangle.top / pixelRatio, - rectangle.right / pixelRatio, - rectangle.bottom / pixelRatio - ); - } - - // - // Callbacks - // - - @Keep - private void onCameraWillChange(boolean animated) { - if (stateCallback != null) { - stateCallback.onCameraWillChange(animated); - } - } - - @Keep - private void onCameraIsChanging() { - if (stateCallback != null) { - stateCallback.onCameraIsChanging(); - } - } - - @Keep - private void onCameraDidChange(boolean animated) { - if (stateCallback != null) { - stateCallback.onCameraDidChange(animated); - } - } - - @Keep - private void onWillStartLoadingMap() { - if (stateCallback != null) { - stateCallback.onWillStartLoadingMap(); - } - } - - @Keep - private void onDidFinishLoadingMap() { - if (stateCallback != null) { - stateCallback.onDidFinishLoadingMap(); - } - } - - @Keep - private void onDidFailLoadingMap(String error) { - if (stateCallback != null) { - stateCallback.onDidFailLoadingMap(error); - } - } - - @Keep - private void onWillStartRenderingFrame() { - if (stateCallback != null) { - stateCallback.onWillStartRenderingFrame(); - } - } - - @Keep - private void onDidFinishRenderingFrame(boolean fully) { - if (stateCallback != null) { - stateCallback.onDidFinishRenderingFrame(fully); - } - } - - @Keep - private void onWillStartRenderingMap() { - if (stateCallback != null) { - stateCallback.onWillStartRenderingMap(); - } - } - - @Keep - private void onDidFinishRenderingMap(boolean fully) { - if (stateCallback != null) { - stateCallback.onDidFinishRenderingMap(fully); - } - } - - @Keep - private void onDidBecomeIdle() { - if (stateCallback != null) { - stateCallback.onDidBecomeIdle(); - } - } - - @Keep - private void onDidFinishLoadingStyle() { - if (stateCallback != null) { - stateCallback.onDidFinishLoadingStyle(); - } - } - - @Keep - private void onSourceChanged(String sourceId) { - if (stateCallback != null) { - stateCallback.onSourceChanged(sourceId); - } - } - - @Keep - private void onStyleImageMissing(String imageId) { - if (stateCallback != null) { - stateCallback.onStyleImageMissing(imageId); - } - } - - @Keep - private boolean onCanRemoveUnusedStyleImage(String imageId) { - if (stateCallback != null) { - return stateCallback.onCanRemoveUnusedStyleImage(imageId); - } - - return true; - } - - @Keep - protected void onSnapshotReady(@Nullable Bitmap mapContent) { - if (checkState("OnSnapshotReady")) { - return; - } - - try { - if (snapshotReadyCallback != null && mapContent != null) { - if (viewCallback == null) { - snapshotReadyCallback.onSnapshotReady(mapContent); - } else { - Bitmap viewContent = viewCallback.getViewContent(); - if (viewContent != null) { - snapshotReadyCallback.onSnapshotReady(BitmapUtils.mergeBitmap(mapContent, viewContent)); - } - } - } - } catch (Throwable err) { - Logger.e(TAG, "Exception in onSnapshotReady", err); - throw err; - } - } - - // - // JNI methods - // - - @Keep - private native void nativeInitialize(NativeMapView nativeMap, - FileSource fileSource, - MapRenderer mapRenderer, - float pixelRatio, - boolean crossSourceCollisions); - - @Keep - private native void nativeDestroy(); - - @Keep - private native void nativeResizeView(int width, int height); - - @Keep - private native void nativeSetStyleUrl(String url); - - @NonNull - @Keep - private native String nativeGetStyleUrl(); - - @Keep - private native void nativeSetStyleJson(String newStyleJson); - - @NonNull - @Keep - private native String nativeGetStyleJson(); - - @Keep - private native void nativeSetLatLngBounds(LatLngBounds latLngBounds); - - @Keep - private native void nativeCancelTransitions(); - - @Keep - private native void nativeSetGestureInProgress(boolean inProgress); - - @Keep - private native void nativeMoveBy(double dx, double dy, long duration); - - @Keep - private native void nativeSetLatLng(double latitude, double longitude, double[] padding, long duration); - - @NonNull - @Keep - private native LatLng nativeGetLatLng(); - - @NonNull - @Keep - private native CameraPosition nativeGetCameraForLatLngBounds( - LatLngBounds latLngBounds, double top, double left, double bottom, double right, double bearing, double tilt); - - @NonNull - @Keep - private native CameraPosition nativeGetCameraForGeometry( - Geometry geometry, double top, double left, double bottom, double right, double bearing, double tilt); - - @Keep - private native void nativeResetPosition(); - - @Keep - private native double nativeGetPitch(); - - @Keep - private native void nativeSetPitch(double pitch, long duration); - - @Keep - private native void nativeSetZoom(double zoom, double cx, double cy, long duration); - - @Keep - private native double nativeGetZoom(); - - @Keep - private native void nativeResetZoom(); - - @Keep - private native void nativeSetMinZoom(double zoom); - - @Keep - private native double nativeGetMinZoom(); - - @Keep - private native void nativeSetMaxZoom(double zoom); - - @Keep - private native double nativeGetMaxZoom(); - - @Keep - private native void nativeRotateBy(double sx, double sy, double ex, double ey, long duration); - - @Keep - private native void nativeSetBearing(double degrees, long duration); - - @Keep - private native void nativeSetBearingXY(double degrees, double fx, double fy, long duration); - - @Keep - private native double nativeGetBearing(); - - @Keep - private native void nativeResetNorth(); - - @Keep - private native void nativeUpdateMarker(long markerId, double lat, double lon, String iconId); - - @NonNull - @Keep - private native long[] nativeAddMarkers(Marker[] markers); - - @NonNull - @Keep - private native long[] nativeAddPolylines(Polyline[] polylines); - - @NonNull - @Keep - private native long[] nativeAddPolygons(Polygon[] polygons); - - @Keep - private native void nativeRemoveAnnotations(long[] id); - - @NonNull - @Keep - private native long[] nativeQueryPointAnnotations(RectF rect); - - @NonNull - @Keep - private native long[] nativeQueryShapeAnnotations(RectF rect); - - @Keep - private native void nativeAddAnnotationIcon(String symbol, int width, int height, float scale, byte[] pixels); - - @Keep - private native void nativeRemoveAnnotationIcon(String symbol); - - @Keep - private native void nativeSetVisibleCoordinateBounds(LatLng[] coordinates, RectF padding, - double direction, long duration); - - @Keep - private native void nativeOnLowMemory(); - - @Keep - private native void nativeSetDebug(boolean debug); - - @Keep - private native void nativeCycleDebugOptions(); - - @Keep - private native boolean nativeGetDebug(); - - @Keep - private native boolean nativeIsFullyLoaded(); - - @Keep - private native void nativeSetReachability(boolean status); - - @Keep - private native double nativeGetMetersPerPixelAtLatitude(double lat, double zoom); - - @NonNull - @Keep - private native ProjectedMeters nativeProjectedMetersForLatLng(double latitude, double longitude); - - @NonNull - @Keep - private native LatLng nativeLatLngForProjectedMeters(double northing, double easting); - - @NonNull - @Keep - private native PointF nativePixelForLatLng(double lat, double lon); - - @NonNull - @Keep - private native LatLng nativeLatLngForPixel(float x, float y); - - @Keep - private native double nativeGetTopOffsetPixelsForAnnotationSymbol(String symbolName); - - @Keep - private native void nativeJumpTo(double angle, double latitude, double longitude, double pitch, double zoom, - double[] padding); - - @Keep - private native void nativeEaseTo(double angle, double latitude, double longitude, - long duration, double pitch, double zoom, double[] padding, - boolean easingInterpolator); - - @Keep - private native void nativeFlyTo(double angle, double latitude, double longitude, - long duration, double pitch, double zoom, double[] padding); - - @NonNull - @Keep - private native CameraPosition nativeGetCameraPosition(); - - @Keep - private native void nativeSetTransitionOptions(TransitionOptions transitionOptions); - - @NonNull - @Keep - private native TransitionOptions nativeGetTransitionOptions(); - - @Keep - private native long nativeGetTransitionDuration(); - - @Keep - private native void nativeSetTransitionDuration(long duration); - - @Keep - private native long nativeGetTransitionDelay(); - - @Keep - private native void nativeSetTransitionDelay(long delay); - - @NonNull - @Keep - private native Layer[] nativeGetLayers(); - - @NonNull - @Keep - private native Layer nativeGetLayer(String layerId); - - @Keep - private native void nativeAddLayer(long layerPtr, String before) throws CannotAddLayerException; - - @Keep - private native void nativeAddLayerAbove(long layerPtr, String above) throws CannotAddLayerException; - - @Keep - private native void nativeAddLayerAt(long layerPtr, int index) throws CannotAddLayerException; - - @Keep - private native boolean nativeRemoveLayer(long layerId); - - @Keep - private native boolean nativeRemoveLayerAt(int index); - - @NonNull - @Keep - private native Source[] nativeGetSources(); - - @NonNull - @Keep - private native Source nativeGetSource(String sourceId); - - @Keep - private native void nativeAddSource(Source source, long sourcePtr) throws CannotAddSourceException; - - @Keep - private native boolean nativeRemoveSource(Source source, long sourcePtr); - - @Keep - private native void nativeAddImage(String name, Bitmap bitmap, float pixelRatio, boolean sdf); - - @Keep - private native void nativeAddImages(Image[] images); - - @Keep - private native void nativeRemoveImage(String name); - - @NonNull - @Keep - private native Bitmap nativeGetImage(String name); - - @Keep - private native void nativeUpdatePolygon(long polygonId, Polygon polygon); - - @Keep - private native void nativeUpdatePolyline(long polylineId, Polyline polyline); - - @Keep - private native void nativeTakeSnapshot(); - - @NonNull - @Keep - private native Feature[] nativeQueryRenderedFeaturesForPoint(float x, float y, - String[] layerIds, - Object[] filter); - - @NonNull - @Keep - private native Feature[] nativeQueryRenderedFeaturesForBox(float left, float top, - float right, float bottom, - String[] layerIds, - Object[] filter); - - @NonNull - @Keep - private native Light nativeGetLight(); - - @Keep - private native void nativeSetPrefetchTiles(boolean enable); - - @Keep - private native boolean nativeGetPrefetchTiles(); - - @Keep - private native void nativeSetPrefetchZoomDelta(int delta); - - @Keep - private native int nativeGetPrefetchZoomDelta(); - - @Override - public long getNativePtr() { - return nativePtr; - } - - // - // Snapshot - // - - @Override - public void addSnapshotCallback(@NonNull MapboxMap.SnapshotReadyCallback callback) { - if (checkState("addSnapshotCallback")) { - return; - } - snapshotReadyCallback = callback; - nativeTakeSnapshot(); - } - - @Override - public void setOnFpsChangedListener(@Nullable final MapboxMap.OnFpsChangedListener listener) { - final Handler handler = new Handler(); - mapRenderer.queueEvent(new Runnable() { - - @Override - public void run() { - if (listener != null) { - mapRenderer.setOnFpsChangedListener(new MapboxMap.OnFpsChangedListener() { - @Override - public void onFpsChanged(final double fps) { - handler.post(new Runnable() { - - @Override - public void run() { - listener.onFpsChanged(fps); - } - - }); - } - }); - } else { - mapRenderer.setOnFpsChangedListener(null); - } - } - }); - } - - @Override - public boolean isDestroyed() { - return destroyed; - } - - private double[] getAnimationPaddingAndClearCachedInsets(double[] providedPadding) { - if (providedPadding == null) { - providedPadding = this.edgeInsets; - } - this.edgeInsets = null; - return providedPadding == null ? null : - new double[] { - providedPadding[1] / pixelRatio, - providedPadding[0] / pixelRatio, - providedPadding[3] / pixelRatio, - providedPadding[2] / pixelRatio - }; - } - - public interface ViewCallback { - @Nullable - Bitmap getViewContent(); - } - - interface StyleCallback { - void onWillStartLoadingMap(); - - void onDidFinishLoadingStyle(); - } - - interface StateCallback extends StyleCallback { - void onCameraWillChange(boolean animated); - - void onCameraIsChanging(); - - void onCameraDidChange(boolean animated); - - void onDidFinishLoadingMap(); - - void onDidFailLoadingMap(String error); - - void onWillStartRenderingFrame(); - - void onDidFinishRenderingFrame(boolean fully); - - void onWillStartRenderingMap(); - - void onDidFinishRenderingMap(boolean fully); - - void onDidBecomeIdle(); - - void onSourceChanged(String sourceId); - - void onStyleImageMissing(String imageId); - - boolean onCanRemoveUnusedStyleImage(String imageId); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java deleted file mode 100644 index 31b459abb..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/OnMapReadyCallback.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.support.annotation.NonNull; - -/** - * Interface definition for a callback to be invoked when the map is ready to be used. - *

- * Once an instance of this interface is set on a {@link MapFragment} or {@link MapView} object, - * the onMapReady(MapboxMap) method is triggered when the map is ready to be used and provides an instance of - * {@link MapboxMap}. - *

- */ -public interface OnMapReadyCallback { - - /** - * Called when the map is ready to be used. - * - * @param mapboxMap An instance of MapboxMap associated with the {@link MapFragment} or - * {@link MapView} that defines the callback. - */ - void onMapReady(@NonNull MapboxMap mapboxMap); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java deleted file mode 100644 index a7e2ace06..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolygonContainer.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - - -import android.support.annotation.NonNull; -import android.support.v4.util.LongSparseArray; - -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.PolygonOptions; - -import java.util.ArrayList; -import java.util.List; - -/** - * Encapsulates {@link Polygon}'s functionality. - */ -class PolygonContainer implements Polygons { - - private final NativeMap nativeMap; - private final LongSparseArray annotations; - - PolygonContainer(NativeMap nativeMap, LongSparseArray annotations) { - this.nativeMap = nativeMap; - this.annotations = annotations; - } - - @Override - public Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap) { - Polygon polygon = polygonOptions.getPolygon(); - if (!polygon.getPoints().isEmpty()) { - long id = nativeMap != null ? nativeMap.addPolygon(polygon) : 0; - polygon.setId(id); - polygon.setMapboxMap(mapboxMap); - annotations.put(id, polygon); - } - return polygon; - } - - @NonNull - @Override - public List addBy(@NonNull List polygonOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polygonOptionsList.size(); - - Polygon polygon; - List polygons = new ArrayList<>(count); - if (nativeMap != null && count > 0) { - for (PolygonOptions polygonOptions : polygonOptionsList) { - polygon = polygonOptions.getPolygon(); - if (!polygon.getPoints().isEmpty()) { - polygons.add(polygon); - } - } - - long[] ids = nativeMap.addPolygons(polygons); - for (int i = 0; i < ids.length; i++) { - polygon = polygons.get(i); - polygon.setMapboxMap(mapboxMap); - polygon.setId(ids[i]); - annotations.put(ids[i], polygon); - } - } - return polygons; - } - - @Override - public void update(@NonNull Polygon polygon) { - nativeMap.updatePolygon(polygon); - annotations.setValueAt(annotations.indexOfKey(polygon.getId()), polygon); - } - - @NonNull - @Override - public List obtainAll() { - List polygons = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Polygon) { - polygons.add((Polygon) annotation); - } - } - return polygons; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java deleted file mode 100644 index 2a0190b5b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polygons.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - - -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.annotations.Polygon; -import com.mapbox.mapboxsdk.annotations.PolygonOptions; - -import java.util.List; - -/** - * Interface that defines convenient methods for working with a {@link Polygon}'s collection. - */ -interface Polygons { - Polygon addBy(@NonNull PolygonOptions polygonOptions, @NonNull MapboxMap mapboxMap); - - List addBy(@NonNull List polygonOptionsList, @NonNull MapboxMap mapboxMap); - - void update(Polygon polygon); - - List obtainAll(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java deleted file mode 100644 index e11da26e0..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/PolylineContainer.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - - -import android.support.annotation.NonNull; -import android.support.v4.util.LongSparseArray; - -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.annotations.PolylineOptions; - -import java.util.ArrayList; -import java.util.List; - -/** - * Encapsulates {@link Polyline}'s functionality. - */ -class PolylineContainer implements Polylines { - - private final NativeMap nativeMap; - private final LongSparseArray annotations; - - PolylineContainer(NativeMap nativeMap, LongSparseArray annotations) { - this.nativeMap = nativeMap; - this.annotations = annotations; - } - - @Override - public Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap) { - Polyline polyline = polylineOptions.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - long id = nativeMap != null ? nativeMap.addPolyline(polyline) : 0; - polyline.setMapboxMap(mapboxMap); - polyline.setId(id); - annotations.put(id, polyline); - } - return polyline; - } - - @NonNull - @Override - public List addBy(@NonNull List polylineOptionsList, @NonNull MapboxMap mapboxMap) { - int count = polylineOptionsList.size(); - Polyline polyline; - List polylines = new ArrayList<>(count); - if (nativeMap != null && count > 0) { - for (PolylineOptions options : polylineOptionsList) { - polyline = options.getPolyline(); - if (!polyline.getPoints().isEmpty()) { - polylines.add(polyline); - } - } - - long[] ids = nativeMap.addPolylines(polylines); - for (int i = 0; i < ids.length; i++) { - Polyline polylineCreated = polylines.get(i); - polylineCreated.setMapboxMap(mapboxMap); - polylineCreated.setId(ids[i]); - annotations.put(ids[i], polylineCreated); - } - } - return polylines; - } - - @Override - public void update(@NonNull Polyline polyline) { - nativeMap.updatePolyline(polyline); - annotations.setValueAt(annotations.indexOfKey(polyline.getId()), polyline); - } - - @NonNull - @Override - public List obtainAll() { - List polylines = new ArrayList<>(); - Annotation annotation; - for (int i = 0; i < annotations.size(); i++) { - annotation = annotations.get(annotations.keyAt(i)); - if (annotation instanceof Polyline) { - polylines.add((Polyline) annotation); - } - } - return polylines; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java deleted file mode 100644 index c9a865cdd..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Polylines.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - - -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.annotations.Polyline; -import com.mapbox.mapboxsdk.annotations.PolylineOptions; - -import java.util.List; - -/** - * Interface that defines convenient methods for working with a {@link Polyline}'s collection. - */ -interface Polylines { - Polyline addBy(@NonNull PolylineOptions polylineOptions, @NonNull MapboxMap mapboxMap); - - List addBy(@NonNull List polylineOptionsList, @NonNull MapboxMap mapboxMap); - - void update(Polyline polyline); - - List obtainAll(); -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java deleted file mode 100644 index 76395f956..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.PointF; -import android.support.annotation.FloatRange; -import android.support.annotation.NonNull; - -import com.mapbox.geojson.Point; -import com.mapbox.mapboxsdk.constants.GeometryConstants; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; -import com.mapbox.mapboxsdk.geometry.ProjectedMeters; -import com.mapbox.mapboxsdk.geometry.VisibleRegion; - -import java.util.ArrayList; -import java.util.List; - -/** - * A projection is used to translate between on screen location and geographic coordinates on - * the surface of the Earth. Screen location is in screen pixels (not display pixels) - * with respect to the top left corner of the map (and not necessarily of the whole screen). - */ -public class Projection { - - @NonNull - private final NativeMap nativeMapView; - @NonNull - private final MapView mapView; - - Projection(@NonNull NativeMap nativeMapView, @NonNull MapView mapView) { - this.nativeMapView = nativeMapView; - this.mapView = mapView; - } - - void setContentPadding(int[] contentPadding) { - double[] output = new double[contentPadding.length]; - for (int i = 0; i < contentPadding.length; i++) { - output[i] = contentPadding[i]; - } - nativeMapView.setContentPadding(output); - } - - int[] getContentPadding() { - double[] padding = nativeMapView.getCameraPosition().padding; - return new int[] {(int) padding[0], (int) padding[1], (int) padding[2], (int) padding[3]}; - } - - /** - * @deprecated unused - */ - @Deprecated - public void invalidateContentPadding() { - } - - /** - * Returns the spherical Mercator projected meters for a LatLng. - */ - @NonNull - public ProjectedMeters getProjectedMetersForLatLng(@NonNull LatLng latLng) { - return nativeMapView.projectedMetersForLatLng(latLng); - } - - /** - * Returns the LatLng for a spherical Mercator projected meters. - */ - @NonNull - public LatLng getLatLngForProjectedMeters(@NonNull ProjectedMeters projectedMeters) { - return nativeMapView.latLngForProjectedMeters(projectedMeters); - } - - /** - *

- * Returns the distance spanned by one pixel at the specified latitude and current zoom level. - *

- * The distance between pixels decreases as the latitude approaches the poles. - * This relationship parallels the relationship between longitudinal coordinates at different latitudes. - * - * @param latitude The latitude for which to return the value. - * @return The distance measured in meters. - */ - public double getMetersPerPixelAtLatitude(@FloatRange(from = -90, to = 90) double latitude) { - return nativeMapView.getMetersPerPixelAtLatitude(latitude); - } - - /** - * Returns the geographic location that corresponds to a screen location. - * The screen location is specified in screen pixels (not display pixels) relative to the - * top left of the map (not the top left of the whole screen). - * - * @param point A Point on the screen in screen pixels. - * @return The LatLng corresponding to the point on the screen, or null if the ray through - * the given screen point does not intersect the ground plane. - */ - @NonNull - public LatLng fromScreenLocation(@NonNull PointF point) { - return nativeMapView.latLngForPixel(point); - } - - /** - * Gets a projection of the viewing frustum for converting between screen coordinates and - * geo-latitude/longitude coordinates. - *

- * This method ignores the content padding. - * - * @return The projection of the viewing frustum in its current state. - */ - @NonNull - public VisibleRegion getVisibleRegion() { - return getVisibleRegion(true); - } - - /** - * Gets a projection of the viewing frustum for converting between screen coordinates and - * geo-latitude/longitude coordinates. - * - * @param ignorePadding True if the padding should be ignored, - * false if the returned region should be reduced by the padding. - * @return The projection of the viewing frustum in its current state. - */ - @NonNull - public VisibleRegion getVisibleRegion(boolean ignorePadding) { - float left; - float right; - float top; - float bottom; - - if (ignorePadding) { - left = 0; - right = mapView.getWidth(); - top = 0; - bottom = mapView.getHeight(); - } else { - int[] contentPadding = getContentPadding(); - left = contentPadding[0]; - right = mapView.getWidth() - contentPadding[2]; - top = contentPadding[1]; - bottom = mapView.getHeight() - contentPadding[3]; - } - - LatLng center = fromScreenLocation(new PointF(left + (right - left) / 2, top + (bottom - top) / 2)); - - LatLng topLeft = fromScreenLocation(new PointF(left, top)); - LatLng topRight = fromScreenLocation(new PointF(right, top)); - LatLng bottomRight = fromScreenLocation(new PointF(right, bottom)); - LatLng bottomLeft = fromScreenLocation(new PointF(left, bottom)); - - List latLngs = new ArrayList<>(); - latLngs.add(topRight); - latLngs.add(bottomRight); - latLngs.add(bottomLeft); - latLngs.add(topLeft); - - double maxEastLonSpan = 0; - double maxWestLonSpan = 0; - - double east = 0; - double west = 0; - double north = GeometryConstants.MIN_LATITUDE; - double south = GeometryConstants.MAX_LATITUDE; - - for (LatLng latLng : latLngs) { - double bearing = bearing(center, latLng); - - if (bearing >= 0) { - double span = getLongitudeSpan(latLng.getLongitude(), center.getLongitude()); - if (span > maxEastLonSpan) { - maxEastLonSpan = span; - east = latLng.getLongitude(); - } - } else { - double span = getLongitudeSpan(center.getLongitude(), latLng.getLongitude()); - if (span > maxWestLonSpan) { - maxWestLonSpan = span; - west = latLng.getLongitude(); - } - } - - if (north < latLng.getLatitude()) { - north = latLng.getLatitude(); - } - if (south > latLng.getLatitude()) { - south = latLng.getLatitude(); - } - } - - if (east < west) { - return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, - LatLngBounds.from(north, east + GeometryConstants.LONGITUDE_SPAN, south, west)); - } - return new VisibleRegion(topLeft, topRight, bottomLeft, bottomRight, - LatLngBounds.from(north, east, south, west)); - } - - /** - * Takes two {@link Point}s and finds the geographic bearing between them. - * - * @param latLng1 the first point used for calculating the bearing - * @param latLng2 the second point used for calculating the bearing - * @return bearing in decimal degrees - * @see Turf Bearing documentation - */ - static double bearing(@NonNull LatLng latLng1, @NonNull LatLng latLng2) { - - double lon1 = degreesToRadians(latLng1.getLongitude()); - double lon2 = degreesToRadians(latLng2.getLongitude()); - double lat1 = degreesToRadians(latLng1.getLatitude()); - double lat2 = degreesToRadians(latLng2.getLatitude()); - - double value1 = Math.sin(lon2 - lon1) * Math.cos(lat2); - double value2 = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) - * Math.cos(lat2) * Math.cos(lon2 - lon1); - - return radiansToDegrees(Math.atan2(value1, value2)); - } - - /** - * Converts an angle in degrees to radians. - * - * @param degrees angle between 0 and 360 degrees - * @return angle in radians - */ - static double degreesToRadians(double degrees) { - double radians = degrees % 360; - return radians * Math.PI / 180; - } - - /** - * Converts an angle in radians to degrees. - * - * @param radians angle in radians - * @return degrees between 0 and 360 degrees - */ - static double radiansToDegrees(double radians) { - double degrees = radians % (2 * Math.PI); - return degrees * 180 / Math.PI; - } - - /** - * Get the absolute distance, in degrees, between the west and - * east boundaries of this LatLngBounds - * - * @return Span distance - */ - static double getLongitudeSpan(double east, double west) { - double longSpan = Math.abs(east - west); - if (east > west) { - return longSpan; - } - - // shortest span contains antimeridian - return GeometryConstants.LONGITUDE_SPAN - longSpan; - } - - /** - * Returns a screen location that corresponds to a geographical coordinate (LatLng). - * The screen location is in screen pixels (not display pixels) relative to the top left - * of the map (not of the whole screen). - * - * @param location A LatLng on the map to convert to a screen location. - * @return A Point representing the screen location in screen pixels. - */ - @NonNull - public PointF toScreenLocation(@NonNull LatLng location) { - return nativeMapView.pixelForLatLng(location); - } - - float getHeight() { - return mapView.getHeight(); - } - - float getWidth() { - return mapView.getWidth(); - } - - /** - * Calculates a zoom level based on minimum scale and current scale from MapView - * - * @param minScale The minimum scale to calculate the zoom level. - * @return zoom level that fits the MapView. - */ - public double calculateZoom(float minScale) { - return nativeMapView.getZoom() + Math.log(minScale) / Math.log(2); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java deleted file mode 100644 index 9c2b97b6a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotationContainer.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.RectF; -import android.support.annotation.NonNull; -import android.support.v4.util.LongSparseArray; - -import com.mapbox.mapboxsdk.annotations.Annotation; - -import java.util.ArrayList; -import java.util.List; - -class ShapeAnnotationContainer implements ShapeAnnotations { - - private final NativeMap nativeMapView; - private final LongSparseArray annotations; - - ShapeAnnotationContainer(NativeMap nativeMapView, LongSparseArray annotations) { - this.nativeMapView = nativeMapView; - this.annotations = annotations; - } - - @NonNull - @Override - public List obtainAllIn(@NonNull RectF rectangle) { - RectF rect = nativeMapView.getDensityDependantRectangle(rectangle); - long[] annotationIds = nativeMapView.queryShapeAnnotations(rect); - return getAnnotationsFromIds(annotationIds); - } - - @NonNull - private List getAnnotationsFromIds(long[] annotationIds) { - List shapeAnnotations = new ArrayList<>(); - for (long annotationId : annotationIds) { - Annotation annotation = annotations.get(annotationId); - if (annotation != null) { - shapeAnnotations.add(annotation); - } - } - return shapeAnnotations; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java deleted file mode 100644 index f9b2ca10d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/ShapeAnnotations.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.RectF; - -import com.mapbox.mapboxsdk.annotations.Annotation; - -import java.util.List; - -interface ShapeAnnotations { - - List obtainAllIn(RectF rectF); - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java deleted file mode 100644 index b21186050..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java +++ /dev/null @@ -1,1150 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.os.AsyncTask; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.annotation.StringDef; -import android.util.DisplayMetrics; -import android.util.Pair; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.layers.TransitionOptions; -import com.mapbox.mapboxsdk.style.light.Light; -import com.mapbox.mapboxsdk.style.sources.Source; -import com.mapbox.mapboxsdk.utils.BitmapUtils; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.ref.WeakReference; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * The proxy object for current map style. - *

- * To create new instances of this object, create a new instance using a {@link Builder} and load the style with - * MapboxMap. This object is returned from {@link MapboxMap#getStyle()} once the style - * has been loaded by underlying map. - *

- */ -@SuppressWarnings("unchecked") -public class Style { - - static final String EMPTY_JSON = "{\"version\": 8,\"sources\": {},\"layers\": []}"; - - private final NativeMap nativeMap; - private final HashMap sources = new HashMap<>(); - private final HashMap layers = new HashMap<>(); - private final HashMap images = new HashMap<>(); - private final Builder builder; - private boolean fullyLoaded; - - /** - * Private constructor to build a style object. - * - * @param builder the builder used for creating this style - * @param nativeMap the map object used to load this style - */ - private Style(@NonNull Builder builder, @NonNull NativeMap nativeMap) { - this.builder = builder; - this.nativeMap = nativeMap; - } - - /** - * Returns the current style url. - * - * @return the style url - * @deprecated use {@link #getUri()} instead - */ - @NonNull - @Deprecated - public String getUrl() { - validateState("getUrl"); - return nativeMap.getStyleUri(); - } - - /** - * Returns the current style uri. - * - * @return the style uri - */ - @NonNull - public String getUri() { - validateState("getUri"); - return nativeMap.getStyleUri(); - } - - /** - * Returns the current style json. - * - * @return the style json - */ - @NonNull - public String getJson() { - validateState("getJson"); - return nativeMap.getStyleJson(); - } - - // - // Source - // - - /** - * Retrieve all the sources in the style - * - * @return all the sources in the current style - */ - @NonNull - public List getSources() { - validateState("getSources"); - return nativeMap.getSources(); - } - - /** - * Adds the source to the map. The source must be newly created and not added to the map before - * - * @param source the source to add - */ - public void addSource(@NonNull Source source) { - validateState("addSource"); - nativeMap.addSource(source); - sources.put(source.getId(), source); - } - - /** - * Retrieve a source by id - * - * @param id the source's id - * @return the source if present in the current style - */ - @Nullable - public Source getSource(String id) { - validateState("getSource"); - Source source = sources.get(id); - if (source == null) { - source = nativeMap.getSource(id); - } - return source; - } - - /** - * Tries to cast the Source to T, throws ClassCastException if it's another type. - * - * @param sourceId the id used to look up a layer - * @param the generic type of a Source - * @return the casted Source, null if another type - */ - @Nullable - public T getSourceAs(@NonNull String sourceId) { - validateState("getSourceAs"); - // noinspection unchecked - if (sources.containsKey(sourceId)) { - return (T) sources.get(sourceId); - } - return (T) nativeMap.getSource(sourceId); - } - - /** - * Removes the source from the style. - * - * @param sourceId the source to remove - * @return the source handle or null if the source was not present - */ - public boolean removeSource(@NonNull String sourceId) { - validateState("removeSource"); - sources.remove(sourceId); - return nativeMap.removeSource(sourceId); - } - - /** - * Removes the source, preserving the reference for re-use - * - * @param source the source to remove - * @return the source - */ - public boolean removeSource(@NonNull Source source) { - validateState("removeSource"); - sources.remove(source.getId()); - return nativeMap.removeSource(source); - } - - // - // Layer - // - - /** - * Adds the layer to the map. The layer must be newly created and not added to the map before - * - * @param layer the layer to add - */ - public void addLayer(@NonNull Layer layer) { - validateState("addLayer"); - nativeMap.addLayer(layer); - layers.put(layer.getId(), layer); - } - - /** - * Adds the layer to the map. The layer must be newly created and not added to the map before - * - * @param layer the layer to add - * @param below the layer id to add this layer before - */ - public void addLayerBelow(@NonNull Layer layer, @NonNull String below) { - validateState("addLayerBelow"); - nativeMap.addLayerBelow(layer, below); - layers.put(layer.getId(), layer); - } - - /** - * Adds the layer to the map. The layer must be newly created and not added to the map before - * - * @param layer the layer to add - * @param above the layer id to add this layer above - */ - public void addLayerAbove(@NonNull Layer layer, @NonNull String above) { - validateState("addLayerAbove"); - nativeMap.addLayerAbove(layer, above); - layers.put(layer.getId(), layer); - } - - /** - * Adds the layer to the map at the specified index. The layer must be newly - * created and not added to the map before - * - * @param layer the layer to add - * @param index the index to insert the layer at - */ - public void addLayerAt(@NonNull Layer layer, @IntRange(from = 0) int index) { - validateState("addLayerAbove"); - nativeMap.addLayerAt(layer, index); - layers.put(layer.getId(), layer); - } - - /** - * Get the layer by id - * - * @param id the layer's id - * @return the layer, if present in the style - */ - @Nullable - public Layer getLayer(@NonNull String id) { - validateState("getLayer"); - Layer layer = layers.get(id); - if (layer == null) { - layer = nativeMap.getLayer(id); - } - return layer; - } - - /** - * Tries to cast the Layer to T, throws ClassCastException if it's another type. - * - * @param layerId the layer id used to look up a layer - * @param the generic attribute of a Layer - * @return the casted Layer, null if another type - */ - @Nullable - public T getLayerAs(@NonNull String layerId) { - validateState("getLayerAs"); - // noinspection unchecked - return (T) nativeMap.getLayer(layerId); - } - - /** - * Retrieve all the layers in the style - * - * @return all the layers in the current style - */ - @NonNull - public List getLayers() { - validateState("getLayers"); - return nativeMap.getLayers(); - } - - /** - * Removes the layer. Any references to the layer become invalid and should not be used anymore - * - * @param layerId the layer to remove - * @return the removed layer or null if not found - */ - public boolean removeLayer(@NonNull String layerId) { - validateState("removeLayer"); - layers.remove(layerId); - return nativeMap.removeLayer(layerId); - } - - /** - * Removes the layer. The reference is re-usable after this and can be re-added - * - * @param layer the layer to remove - * @return the layer - */ - public boolean removeLayer(@NonNull Layer layer) { - validateState("removeLayer"); - layers.remove(layer.getId()); - return nativeMap.removeLayer(layer); - } - - /** - * Removes the layer. Any other references to the layer become invalid and should not be used anymore - * - * @param index the layer index - * @return the removed layer or null if not found - */ - public boolean removeLayerAt(@IntRange(from = 0) int index) { - validateState("removeLayerAt"); - return nativeMap.removeLayerAt(index); - } - - // - // Image - // - - /** - * Adds an image to be used in the map's style - * - * @param name the name of the image - * @param image the pre-multiplied Bitmap - */ - public void addImage(@NonNull String name, @NonNull Bitmap image) { - addImage(name, image, false); - } - - /** - * Adds an drawable to be converted into a bitmap to be used in the map's style - * - * @param name the name of the image - * @param drawable the drawable instance to convert - */ - public void addImage(@NonNull String name, @NonNull Drawable drawable) { - Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable); - if (bitmap == null) { - throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap."); - } - addImage(name, bitmap, false); - } - - /** - * Adds an image to be used in the map's style - * - * @param name the name of the image - * @param bitmap the pre-multiplied Bitmap - * @param sdf the flag indicating image is an SDF or template image - */ - public void addImage(@NonNull final String name, @NonNull Bitmap bitmap, boolean sdf) { - validateState("addImage"); - nativeMap.addImages(new Image[]{toImage(new Builder.ImageWrapper(name, bitmap, sdf))}); - } - - /** - * Adds an image asynchronously, to be used in the map's style. - * - * @param name the name of the image - * @param image the pre-multiplied Bitmap - */ - public void addImageAsync(@NonNull String name, @NonNull Bitmap image) { - addImageAsync(name, image, false); - } - - /** - * Adds an drawable asynchronously, to be converted into a bitmap to be used in the map's style. - * - * @param name the name of the image - * @param drawable the drawable instance to convert - */ - public void addImageAsync(@NonNull String name, @NonNull Drawable drawable) { - Bitmap bitmap = BitmapUtils.getBitmapFromDrawable(drawable); - if (bitmap == null) { - throw new IllegalArgumentException("Provided drawable couldn't be converted to a Bitmap."); - } - addImageAsync(name, bitmap, false); - } - - /** - * Adds an image asynchronously, to be used in the map's style. - * - * @param name the name of the image - * @param bitmap the pre-multiplied Bitmap - * @param sdf the flag indicating image is an SDF or template image - */ - public void addImageAsync(@NonNull final String name, @NonNull Bitmap bitmap, boolean sdf) { - validateState("addImage"); - new BitmapImageConversionTask(nativeMap).execute(new Builder.ImageWrapper(name, bitmap, sdf)); - } - - /** - * Adds images to be used in the map's style. - * - * @param images the map of images to add - */ - public void addImages(@NonNull HashMap images) { - addImages(images, false); - } - - /** - * Adds images to be used in the map's style. - * - * @param images the map of images to add - * @param sdf the flag indicating image is an SDF or template image - */ - public void addImages(@NonNull HashMap images, boolean sdf) { - validateState("addImage"); - Image[] convertedImages = new Image[images.size()]; - int index = 0; - for (Builder.ImageWrapper imageWrapper : Builder.ImageWrapper.convertToImageArray(images, sdf)) { - convertedImages[index] = toImage(imageWrapper); - index++; - } - - nativeMap.addImages(convertedImages); - } - - /** - * Adds images asynchronously, to be used in the map's style. - * - * @param images the map of images to add - */ - public void addImagesAsync(@NonNull HashMap images) { - addImagesAsync(images, false); - } - - /** - * Adds images asynchronously, to be used in the map's style. - * - * @param images the map of images to add - * @param sdf the flag indicating image is an SDF or template image - */ - public void addImagesAsync(@NonNull HashMap images, boolean sdf) { - validateState("addImages"); - new BitmapImageConversionTask(nativeMap).execute(Builder.ImageWrapper.convertToImageArray(images, sdf)); - } - - /** - * Removes an image from the map's style. - * - * @param name the name of the image to remove - */ - public void removeImage(@NonNull String name) { - validateState("removeImage"); - nativeMap.removeImage(name); - } - - /** - * Get an image from the map's style using an id. - * - * @param id the id of the image - * @return the image bitmap - */ - @Nullable - public Bitmap getImage(@NonNull String id) { - validateState("getImage"); - return nativeMap.getImage(id); - } - - // - // Transition - // - - /** - *

- * Set the transition options for style changes. - *

- * If not set, any changes take effect without animation, besides symbols, - * which will fade in/out with a default duration after symbol collision detection. - *

- * To disable symbols fade in/out animation, - * pass transition options with {@link TransitionOptions#enablePlacementTransitions} equal to false. - *

- * Both {@link TransitionOptions#duration} and {@link TransitionOptions#delay} - * will also change the behavior of the symbols fade in/out animation if the placement transition is enabled. - * - * @param transitionOptions the transition options - */ - public void setTransition(@NonNull TransitionOptions transitionOptions) { - validateState("setTransition"); - nativeMap.setTransitionOptions(transitionOptions); - } - - /** - *

- * Get the transition options for style changes. - *

- * By default, any changes take effect without animation, besides symbols, - * which will fade in/out with a default duration after symbol collision detection. - *

- * To disable symbols fade in/out animation, - * pass transition options with {@link TransitionOptions#enablePlacementTransitions} equal to false - * into {@link #setTransition(TransitionOptions)}. - *

- * Both {@link TransitionOptions#duration} and {@link TransitionOptions#delay} - * will also change the behavior of the symbols fade in/out animation if the placement transition is enabled. - * - * @return TransitionOptions the transition options - */ - @NonNull - public TransitionOptions getTransition() { - validateState("getTransition"); - return nativeMap.getTransitionOptions(); - } - - // - // Light - // - - /** - * Get the light source used to change lighting conditions on extruded fill layers. - * - * @return the global light source - */ - @Nullable - public Light getLight() { - validateState("getLight"); - return nativeMap.getLight(); - } - - // - // State - // - - /** - * Called when the underlying map will start loading a new style or the map is destroyed. - * This method will clean up this style by setting the java sources and layers - * in a detached state and removing them from core. - */ - void clear() { - fullyLoaded = false; - for (Layer layer : layers.values()) { - if (layer != null) { - layer.setDetached(); - } - } - - for (Source source : sources.values()) { - if (source != null) { - source.setDetached(); - } - } - - for (Map.Entry bitmapEntry : images.entrySet()) { - nativeMap.removeImage(bitmapEntry.getKey()); - bitmapEntry.getValue().recycle(); - } - - sources.clear(); - layers.clear(); - images.clear(); - } - - /** - * Called when the underlying map has finished loading this style. - * This method will add all components added to the builder that were defined with the 'with' prefix. - */ - void onDidFinishLoadingStyle() { - if (!fullyLoaded) { - fullyLoaded = true; - for (Source source : builder.sources) { - addSource(source); - } - - for (Builder.LayerWrapper layerWrapper : builder.layers) { - if (layerWrapper instanceof Builder.LayerAtWrapper) { - addLayerAt(layerWrapper.layer, ((Builder.LayerAtWrapper) layerWrapper).index); - } else if (layerWrapper instanceof Builder.LayerAboveWrapper) { - addLayerAbove(layerWrapper.layer, ((Builder.LayerAboveWrapper) layerWrapper).aboveLayer); - } else if (layerWrapper instanceof Builder.LayerBelowWrapper) { - addLayerBelow(layerWrapper.layer, ((Builder.LayerBelowWrapper) layerWrapper).belowLayer); - } else { - // just add layer to map, but below annotations - addLayerBelow(layerWrapper.layer, MapboxConstants.LAYER_ID_ANNOTATIONS); - } - } - - for (Builder.ImageWrapper image : builder.images) { - addImage(image.id, image.bitmap, image.sdf); - } - - if (builder.transitionOptions != null) { - setTransition(builder.transitionOptions); - } - } - } - - /** - * Returns true if the style is fully loaded. Returns false if style hasn't been fully loaded or a new style is - * underway of being loaded. - * - * @return True if fully loaded, false otherwise - */ - public boolean isFullyLoaded() { - return fullyLoaded; - } - - /** - * Validates the style state, throw an IllegalArgumentException on invalid state. - * - * @param methodCall the calling method name - */ - private void validateState(String methodCall) { - if (!fullyLoaded) { - throw new IllegalStateException( - String.format("Calling %s when a newer style is loading/has loaded.", methodCall) - ); - } - } - - // - // Builder - // - - /** - * Builder for composing a style object. - */ - public static class Builder { - - private final List sources = new ArrayList<>(); - private final List layers = new ArrayList<>(); - private final List images = new ArrayList<>(); - - private TransitionOptions transitionOptions; - private String styleUri; - private String styleJson; - - /** - *

- * Will loads a new map style asynchronous from the specified URL. - *

- * {@code url} can take the following forms: - *
    - *
  • {@code Style#StyleUrl}: load one of the bundled styles in {@link Style}.
  • - *
  • {@code mapbox://styles// - \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/AccountsManagerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/AccountsManagerTest.java deleted file mode 100644 index 5ce71ceac..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/AccountsManagerTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.content.SharedPreferences; -import android.text.format.DateUtils; - -import org.junit.Test; - -import static com.mapbox.mapboxsdk.constants.MapboxConstants.KEY_PREFERENCE_SKU_TOKEN; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.RETURNS_DEEP_STUBS; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class AccountsManagerTest { - @Test - public void testIsExpired() { - long now = AccountsManager.getNow(); - - long defaultValue = 0L; - long tooOld = now - DateUtils.HOUR_IN_MILLIS - 1; - long futureValue = now + 1; - long immediatePast = now - 1; - - assertTrue(AccountsManager.isExpired(now, defaultValue)); - assertTrue(AccountsManager.isExpired(now, tooOld)); - - assertFalse(AccountsManager.isExpired(now, futureValue)); - assertFalse(AccountsManager.isExpired(now, immediatePast)); - } - - @Test - public void checksSkuTokenExternalManagement() { - SharedPreferences mockedSharedPreferences = mock(SharedPreferences.class); - when(mockedSharedPreferences.getString(KEY_PREFERENCE_SKU_TOKEN, "")).thenReturn("external-sku-token"); - boolean isNotManaged = false; - AccountsManager theAccountsManager = new AccountsManager(mockedSharedPreferences, isNotManaged); - - String skuToken = theAccountsManager.getSkuToken(); - - assertEquals("external-sku-token", skuToken); - } - - @Test - public void checksSkuTokenInternalManagement() { - SharedPreferences mockedSharedPreferences = mock(SharedPreferences.class, RETURNS_DEEP_STUBS); - when(mockedSharedPreferences.getString("com.mapbox.mapboxsdk.accounts.userid", "")) - .thenReturn("any-user-id"); - boolean isManaged = true; - AccountsManager theAccountsManager = new AccountsManager(mockedSharedPreferences, isManaged); - - String skuToken = theAccountsManager.getSkuToken(); - - assertFalse(skuToken.isEmpty()); - assertTrue(skuToken.startsWith("100")); - assertTrue(skuToken.endsWith("any-user-id")); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxInjector.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxInjector.java deleted file mode 100644 index 966c75631..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxInjector.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.lang.reflect.Field; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MapboxInjector { - - private static final String FIELD_INSTANCE = "INSTANCE"; - private static final String FIELD_ACCOUNTS = "accounts"; - - public static void inject(@NonNull Context context, @NonNull String accessToken) { - inject(context, accessToken, null); - } - - public static void inject(@NonNull Context context, @NonNull String accessToken, @Nullable String skuToken) { - Mapbox mapbox = new Mapbox(context, accessToken); - try { - Field instance = Mapbox.class.getDeclaredField(FIELD_INSTANCE); - instance.setAccessible(true); - instance.set(mapbox, mapbox); - - Field accounts = Mapbox.class.getDeclaredField(FIELD_ACCOUNTS); - accounts.setAccessible(true); - - AccountsManager manager = mock(AccountsManager.class); - when(manager.getSkuToken()).thenReturn(skuToken); - accounts.set(mapbox, manager); - } catch (Exception exception) { - throw new AssertionError(); - } - } - - public static void clear() { - try { - Field field = Mapbox.class.getDeclaredField(FIELD_INSTANCE); - field.setAccessible(true); - field.set(field, null); - } catch (Exception exception) { - throw new AssertionError(); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java deleted file mode 100644 index a4b573672..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/MapboxTest.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.mapbox.mapboxsdk; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.util.AttributeSet; -import android.util.DisplayMetrics; - -import com.mapbox.mapboxsdk.exceptions.MapboxConfigurationException; -import com.mapbox.mapboxsdk.maps.MapView; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import static junit.framework.TestCase.assertNotNull; -import static junit.framework.TestCase.assertSame; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class MapboxTest { - - private Context context; - private Context appContext; - - @Rule - public ExpectedException expectedException = ExpectedException.none(); - - @Before - public void before() { - context = mock(Context.class); - appContext = mock(Context.class); - when(context.getApplicationContext()).thenReturn(appContext); - } - - @Test - public void testGetAccessToken() { - final String accessToken = "pk.0000000001"; - MapboxInjector.inject(context, accessToken); - assertSame(accessToken, Mapbox.getAccessToken()); - } - - @Test - public void testApplicationContext() { - MapboxInjector.inject(context, "pk.0000000001"); - assertNotNull(Mapbox.getApplicationContext()); - assertNotEquals(context, appContext); - assertEquals(appContext, appContext); - } - - @Test - public void testPkTokenValid() { - assertTrue(Mapbox.isAccessTokenValid("pk.0000000001")); - } - - @Test - public void testSkTokenValid() { - assertTrue(Mapbox.isAccessTokenValid("sk.0000000001")); - } - - @Test - public void testEmptyToken() { - assertFalse(Mapbox.isAccessTokenValid("")); - } - - @Test - public void testNullToken() { - assertFalse(Mapbox.isAccessTokenValid(null)); - } - - @Test - public void testBlaBlaToken() { - assertFalse(Mapbox.isAccessTokenValid("blabla")); - } - - @Test - public void testNoInstance() { - DisplayMetrics displayMetrics = mock(DisplayMetrics.class); - Resources resources = mock(Resources.class); - when(resources.getDisplayMetrics()).thenReturn(displayMetrics); - when(context.getResources()).thenReturn(resources); - TypedArray typedArray = mock(TypedArray.class); - when(context.obtainStyledAttributes(nullable(AttributeSet.class), any(int[].class), anyInt(), anyInt())) - .thenReturn(typedArray); - - expectedException.expect(MapboxConfigurationException.class); - expectedException.expectMessage( - "\nUsing MapView requires calling Mapbox.getInstance(Context context, String accessToken) before " - + "inflating or creating the view. The access token parameter is required when using a Mapbox service." - + "\nPlease see https://www.mapbox.com/help/create-api-access-token/ to learn how to create one." - + "\nMore information in this guide https://www.mapbox.com/help/first-steps-android-sdk/#access-tokens." - ); - new MapView(context); - } - - @After - public void after() { - MapboxInjector.clear(); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java deleted file mode 100644 index 0c61b01be..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/AnnotationTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.support.annotation.NonNull; -import com.mapbox.mapboxsdk.maps.MapboxMap; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.InjectMocks; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -public class AnnotationTest { - - @InjectMocks - private MapboxMap mapboxMap = mock(MapboxMap.class); - private Annotation annotation; - @NonNull - private Annotation compare = new Annotation() { - @Override - public long getId() { - return 1; - } - }; - - @Before - public void beforeTest() { - annotation = new Annotation() { - // empty child - }; - } - - @Test - public void testSanity() { - assertNotNull("markerOptions should not be null", annotation); - } - - @Test - public void testRemove() { - annotation.setId(1); - annotation.setMapboxMap(mapboxMap); - annotation.remove(); - verify(mapboxMap, times(1)).removeAnnotation(annotation); - } - - @Test - public void testRemoveUnboundMapboxMap() { - annotation.setId(1); - annotation.remove(); - verify(mapboxMap, times(0)).removeAnnotation(annotation); - } - - @Test - public void testCompareToEqual() { - annotation.setId(1); - assertEquals("conparable equal", 0, annotation.compareTo(compare)); - } - - @Test - public void testCompareToHigher() { - annotation.setId(3); - assertEquals("conparable higher", -1, annotation.compareTo(compare)); - } - - @Test - public void testCompareTolower() { - annotation.setId(0); - assertEquals("conparable lower", 1, annotation.compareTo(compare)); - } - - @Test - public void testEquals() { - Annotation holder = null; - assertFalse(annotation.equals(holder)); - holder = annotation; - assertTrue(annotation.equals(holder)); - assertFalse(annotation.equals(new Object())); - } - - @Test - public void testHashcode() { - int id = 1; - annotation.setId(id); - assertSame("hashcode should match", annotation.hashCode(), id); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java deleted file mode 100644 index e56ca559d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/IconTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Bitmap; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNotSame; -import static org.mockito.Mockito.when; - -public class IconTest { - - @Mock - Bitmap bitmap; - - @Before - public void beforeTest() { - MockitoAnnotations.initMocks(this); - when(bitmap.getConfig()).thenReturn(Bitmap.Config.ARGB_8888); - } - - @Test - public void testId() { - String id = "test"; - Icon icon = IconFactory.recreate(id, Bitmap.createBitmap(0, 0, Bitmap.Config.ALPHA_8)); - assertEquals("id should match", id, icon.getId()); - } - - @Test - public void testBitmap() { - Icon icon = IconFactory.recreate("test", bitmap); - assertEquals("bitmap should match", bitmap, icon.getBitmap()); - } - - @Test - public void testEquals() { - Icon icon1 = IconFactory.recreate("test", bitmap); - Icon icon2 = IconFactory.recreate("test", bitmap); - assertEquals("icons should not match", icon1, icon2); - } - - @Test - public void testEqualsObject() { - Icon icon = IconFactory.recreate("test", Bitmap.createBitmap(0, 0, Bitmap.Config.ALPHA_8)); - assertNotSame("icon should not match", new Object(), icon); - } - - @Test - public void testHashcode() { - Icon icon = IconFactory.recreate("test", bitmap); - long expectedHashcode = 31 * bitmap.hashCode() + "test".hashCode(); - assertEquals("hashcode should match", expectedHashcode, icon.hashCode()); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java deleted file mode 100644 index 94b629860..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/InfoWindowTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.PointF; - -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapView; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; - -import org.junit.Test; -import org.mockito.InjectMocks; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class InfoWindowTest { - - @InjectMocks - MapView mMapView = mock(MapView.class); - - @InjectMocks - MapboxMap mMapboxMap = mock(MapboxMap.class); - - @Test - public void testSanity() { - InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap); - assertNotNull("infoWindow should exist", infoWindow); - } - - @Test - public void testBoundMarker() { - MarkerOptions markerOptions = new MarkerOptions(); - Marker marker = markerOptions.position(new LatLng()).getMarker(); - InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap).setBoundMarker(marker); - assertEquals("marker should match", marker, infoWindow.getBoundMarker()); - } - - @Test - public void testClose() { - InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap); - infoWindow.close(); - assertEquals("infowindow should not be visible", false, infoWindow.isVisible()); - } - - - @Test - public void testOpen() { - LatLng latLng = new LatLng(0, 0); - Projection projection = mock(Projection.class); - when(mMapboxMap.getProjection()).thenReturn(projection); - when(projection.toScreenLocation(latLng)).thenReturn(new PointF(0, 0)); - - InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap); - infoWindow.open(mMapView, new MarkerOptions().position(new LatLng()).getMarker(), latLng, 0, 0); - assertEquals("infowindow should not be visible", true, infoWindow.isVisible()); - } - - @Test - public void testOpenClose() { - LatLng latLng = new LatLng(0, 0); - Projection projection = mock(Projection.class); - when(mMapboxMap.getProjection()).thenReturn(projection); - when(projection.toScreenLocation(latLng)).thenReturn(new PointF(0, 0)); - - InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap); - infoWindow.open(mMapView, new MarkerOptions().position(new LatLng()).getMarker(), latLng, 0, 0); - infoWindow.close(); - assertEquals("infowindow should not be visible", false, infoWindow.isVisible()); - } - - - @Test - public void testUpdate() { - LatLng latLng = new LatLng(0, 0); - Projection projection = mock(Projection.class); - when(mMapboxMap.getProjection()).thenReturn(projection); - when(projection.toScreenLocation(latLng)).thenReturn(new PointF(0, 0)); - - InfoWindow infoWindow = new InfoWindow(mMapView, mMapboxMap); - infoWindow.open(mMapView, new MarkerOptions().position(latLng).getMarker(), latLng, 0, 0); - infoWindow.update(); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java deleted file mode 100644 index fa571e06b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/MarkerTest.java +++ /dev/null @@ -1,160 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import android.graphics.Bitmap; -import android.os.Parcelable; - -import com.mapbox.mapboxsdk.exceptions.InvalidMarkerPositionException; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.utils.MockParcel; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; - -public class MarkerTest { - - @Test - public void testSanity() { - MarkerOptions markerOptions = new MarkerOptions(); - assertNotNull("markerOptions should not be null", markerOptions); - } - - @Test - public void testMarker() { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()); - assertNotNull("marker should not be null", markerOptions.getMarker()); - } - - @Test(expected = InvalidMarkerPositionException.class) - public void testInvalidMarker() { - new MarkerOptions().getMarker(); - } - - @Test - public void testPosition() { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(10, 12)); - Marker marker = markerOptions.getMarker(); - assertEquals(marker.getPosition(), new LatLng(10, 12)); - assertEquals(markerOptions.getPosition(), new LatLng(10, 12)); - } - - @Test - public void testTitle() { - MarkerOptions markerOptions = new MarkerOptions().title("Mapbox").position(new LatLng()); - Marker marker = markerOptions.getMarker(); - assertEquals(marker.getTitle(), "Mapbox"); - assertEquals(markerOptions.getTitle(), "Mapbox"); - } - - @Test - public void testSnippet() { - MarkerOptions markerOptions = new MarkerOptions().snippet("Mapbox").position(new LatLng()); - Marker marker = markerOptions.getMarker(); - assertEquals(marker.getSnippet(), "Mapbox"); - } - - @Test - public void testBuilder() { - Marker marker = new MarkerOptions().title("title").snippet("snippet").position(new LatLng(10, 12)).getMarker(); - assertEquals(marker.getSnippet(), "snippet"); - - assertEquals(marker.getPosition(), new LatLng(10, 12)); - } - - @Test - public void testIcon() { - Bitmap bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_4444); - Icon icon = IconFactory.recreate("test", bitmap); - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()).icon(icon); - Marker marker = markerOptions.getMarker(); - assertEquals("Icon should match", icon, marker.getIcon()); - assertEquals("Icon should match", icon, markerOptions.getIcon()); - } - - @Test - public void testHashCode() { - Marker marker = new MarkerOptions().position(new LatLng()).getMarker(); - assertEquals("hash code should match", marker.hashCode(), 0); - } - - @Test - public void testHashCodeBuilder() { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(10, 12)); - assertEquals("hash code should match", markerOptions.hashCode(), 579999617); - } - - @Test - public void testEquals() { - Marker markerOne = new MarkerOptions().position(new LatLng(0, 0)).getMarker(); - Marker markerTwo = new MarkerOptions().position(new LatLng(0, 0)).getMarker(); - assertEquals(markerOne, markerTwo); - } - - @Test - public void testEqualityDifferentLocation() { - MarkerOptions marker = new MarkerOptions().position(new LatLng(0, 0)); - MarkerOptions other = new MarkerOptions().position(new LatLng(1, 0)); - assertNotEquals("Should not match", other, marker); - } - - - @Test - public void testEqualityDifferentSnippet() { - MarkerOptions marker = new MarkerOptions().snippet("s"); - MarkerOptions other = new MarkerOptions(); - assertNotEquals("Should not match", other, marker); - } - - @Test - public void testEqualityDifferentIcon() { - MarkerOptions marker = new MarkerOptions().icon(mock(Icon.class)); - MarkerOptions other = new MarkerOptions(); - assertNotEquals("Should not match", other, marker); - } - - @Test - public void testEqualityDifferentTitle() { - MarkerOptions marker = new MarkerOptions().title("t"); - MarkerOptions other = new MarkerOptions(); - assertNotEquals("Should not match", other, marker); - } - - @Test - public void testEqualsItself() { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(0, 0)); - Marker marker = markerOptions.getMarker(); - assertEquals("Marker should match", marker, marker); - assertEquals("MarkerOptions should match", markerOptions, markerOptions); - } - - @Test - public void testNotEquals() { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng(0, 0)); - Marker marker = markerOptions.getMarker(); - assertNotEquals("MarkerOptions should match", markerOptions, new Object()); - assertNotEquals("Marker should match", marker, new Object()); - } - - @Test - public void testEqualityBuilder() { - MarkerOptions markerOne = new MarkerOptions().position(new LatLng(0, 0)); - MarkerOptions markerTwo = new MarkerOptions().position(new LatLng(0, 0)); - assertEquals(markerOne, markerTwo); - } - - @Test - public void testToString() { - Marker marker = new MarkerOptions().position(new LatLng(0, 0)).getMarker(); - assertEquals(marker.toString(), "Marker [position[" + "LatLng [latitude=0.0, longitude=0.0, altitude=0.0]" + "]]"); - } - - @Test - public void testParcelable() { - MarkerOptions markerOptions = new MarkerOptions().position(new LatLng()).title("t").snippet("s"); - Parcelable parcelable = MockParcel.obtain(markerOptions); - assertEquals("Parcel should match original object", parcelable, markerOptions); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java deleted file mode 100644 index 3933c6888..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolygonTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class PolygonTest { - - @Test - public void testSanity() { - PolygonOptions polygonOptions = new PolygonOptions(); - assertNotNull("polygonOptions should not be null", polygonOptions); - } - - @Test - public void testPolygon() { - Polygon polygon = new PolygonOptions().getPolygon(); - assertNotNull("polyline should not be null", polygon); - } - - @Test - public void testAlpha() { - Polygon polygon = new PolygonOptions().alpha(0.5f).getPolygon(); - assertEquals(0.5f, polygon.getAlpha(), 0.0f); - } - - @Test - public void testStrokeColor() { - Polygon polygon = new PolygonOptions().strokeColor(1).getPolygon(); - assertEquals(1, polygon.getStrokeColor()); - } - - @Test - public void testFillColor() { - Polygon polygon = new PolygonOptions().fillColor(1).getPolygon(); - assertEquals(1, polygon.getFillColor()); - } - - @Test - public void testLatLng() { - Polygon polygon = new PolygonOptions().add(new LatLng(0, 0)).getPolygon(); - assertNotNull("points should not be null", polygon.getPoints()); - assertEquals(new LatLng(0, 0), polygon.getPoints().get(0)); - } - - @Test - public void testAddAllLatLng() { - List coordinates = new ArrayList<>(); - coordinates.add(new LatLng(0, 0)); - Polygon polygon = new PolygonOptions().addAll(coordinates).getPolygon(); - assertNotNull(polygon.getPoints()); - assertEquals(new LatLng(0, 0), polygon.getPoints().get(0)); - } - - @Test - public void testBuilder() { - PolylineOptions polylineOptions = new PolylineOptions(); - polylineOptions.width(1.0f); - polylineOptions.color(2); - polylineOptions.add(new LatLng(0, 0)); - - Polyline polyline = polylineOptions.getPolyline(); - assertEquals(1.0f, polyline.getWidth(), 0); - assertEquals(2, polyline.getColor()); - assertNotNull("Points should not be null", polyline.getPoints()); - assertEquals(new LatLng(0, 0), polyline.getPoints().get(0)); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java deleted file mode 100644 index 54bb0e8cf..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/annotations/PolylineTest.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.mapbox.mapboxsdk.annotations; - -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class PolylineTest { - - @Test - public void testSanity() { - PolylineOptions polylineOptions = new PolylineOptions(); - assertNotNull("polylineOptions should not be null", polylineOptions); - } - - @Test - public void testPolyline() { - Polyline polyline = new PolylineOptions().getPolyline(); - assertNotNull("polyline should not be null", polyline); - } - - @Test - public void testAlpha() { - Polyline polyline = new PolylineOptions().alpha(0.2f).getPolyline(); - assertEquals(0.2f, polyline.getAlpha(), 0.0f); - } - - @Test - public void testWidth() { - Polyline polyline = new PolylineOptions().width(1).getPolyline(); - assertEquals(1.0f, polyline.getWidth(), 0); - } - - @Test - public void testColor() { - Polyline polyline = new PolylineOptions().color(1).getPolyline(); - assertEquals(1, polyline.getColor()); - } - - @Test - public void testAddLatLng() { - Polyline polyline = new PolylineOptions().add(new LatLng(0, 0)).getPolyline(); - assertNotNull("Points should not be null", polyline.getPoints()); - assertEquals(new LatLng(0, 0), polyline.getPoints().get(0)); - } - - @Test - public void testAddAllLatLng() { - List coordinates = new ArrayList<>(); - coordinates.add(new LatLng(0, 0)); - Polyline polyline = new PolylineOptions().addAll(coordinates).getPolyline(); - assertNotNull(polyline.getPoints()); - assertEquals(new LatLng(0, 0), polyline.getPoints().get(0)); - } - - @Test - public void testBuilder() { - PolylineOptions polylineOptions = new PolylineOptions(); - polylineOptions.width(1.0f); - polylineOptions.color(2); - polylineOptions.add(new LatLng(0, 0)); - - Polyline polyline = polylineOptions.getPolyline(); - assertEquals(1.0f, polyline.getWidth(), 0); - assertEquals(2, polyline.getColor()); - assertNotNull("Points should not be null", polyline.getPoints()); - assertEquals(new LatLng(0, 0), polyline.getPoints().get(0)); - } - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java deleted file mode 100644 index b72e85270..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/attribution/AttributionParseTest.java +++ /dev/null @@ -1,322 +0,0 @@ -package com.mapbox.mapboxsdk.attribution; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.Set; - -import static junit.framework.Assert.assertEquals; - -@RunWith(RobolectricTestRunner.class) -public class AttributionParseTest { - - private static final String STREETS_ATTRIBUTION = "© Mapbox © OpenStreetMap Improve this map\n"; - private static final String SATELLITE_ATTRIBUTION = "© Mapbox © OpenStreetMap Improve this map © DigitalGlobe\n"; - - @Test - public void testParseAttributionStringSatellite() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(SATELLITE_ATTRIBUTION) - .build(); - - Set attributionList = attributionParser.getAttributions(); - assertEquals("Size of list should match", 4, attributionList.size()); - - int counter = 0; - for (Attribution attribution : attributionList) { - switch (counter) { - case 0: - assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl()); - assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle()); - break; - case 1: - assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl()); - assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle()); - break; - case 2: - assertEquals("URL improve map should match", "https://apps.mapbox.com/feedback/", attribution.getUrl()); - assertEquals("Title improve map should match", "Improve This Map", attribution.getTitle()); - break; - case 3: - assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl()); - assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle()); - break; - } - counter++; - } - } - - @Test - public void testParseAttributionStringStreets() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION) - .build(); - - Set attributionList = attributionParser.getAttributions(); - assertEquals("Size of list should match", 3, attributionList.size()); - - int counter = 0; - for (Attribution attribution : attributionList) { - switch (counter) { - case 0: - assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl()); - assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle()); - break; - case 1: - assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl()); - assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle()); - break; - case 2: - assertEquals("URL improve map should match", "https://apps.mapbox.com/feedback/", attribution.getUrl()); - assertEquals("Title improve map should match", "Improve This Map", attribution.getTitle()); - break; - } - counter++; - } - } - - @Test - public void testParseAttributionWithoutMapbox() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION) - .withMapboxAttribution(false) - .build(); - - Set attributionList = attributionParser.getAttributions(); - assertEquals("Size of list should match", 2, attributionList.size()); - - int counter = 0; - for (Attribution attribution : attributionList) { - switch (counter) { - case 0: - assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl()); - assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle()); - break; - case 1: - assertEquals("URL improve map should match", "https://apps.mapbox.com/feedback/", attribution.getUrl()); - assertEquals("Title improve map should match", "Improve This Map", attribution.getTitle()); - break; - } - counter++; - } - } - - @Test - public void testParseAttributionArrayString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(new String[] {STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION}) - .build(); - Set attributionList = attributionParser.getAttributions(); - assertEquals("Size of list should match", 4, attributionList.size()); - - int counter = 0; - for (Attribution attribution : attributionList) { - switch (counter) { - case 0: - assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl()); - assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle()); - break; - case 1: - assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl()); - assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle()); - break; - case 2: - assertEquals("URL improve map should match", "https://apps.mapbox.com/feedback/", attribution.getUrl()); - assertEquals("Title improve map should match", "Improve This Map", attribution.getTitle()); - break; - case 3: - assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl()); - assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle()); - break; - } - counter++; - } - } - - @Test - public void testHideImproveThisMapAttributionArrayString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(SATELLITE_ATTRIBUTION) - .withImproveMap(false) - .build(); - Set attributionList = attributionParser.getAttributions(); - assertEquals("Size of list should match", 3, attributionList.size()); - - int counter = 0; - for (Attribution attribution : attributionList) { - switch (counter) { - case 0: - assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl()); - assertEquals("Title mapbox should match", "© Mapbox", attribution.getTitle()); - break; - case 1: - assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl()); - assertEquals("Title openstreetmap should match", "© OpenStreetMap", attribution.getTitle()); - break; - case 2: - assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl()); - assertEquals("Title digital globe should match", "© DigitalGlobe", attribution.getTitle()); - break; - } - counter++; - } - } - - @Test - public void testParseHideCopyrightAttributionArrayString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION, "", SATELLITE_ATTRIBUTION) - .withCopyrightSign(false) - .build(); - Set attributionList = attributionParser.getAttributions(); - assertEquals("Size of list should match", 4, attributionList.size()); - - int counter = 0; - for (Attribution attribution : attributionList) { - switch (counter) { - case 0: - assertEquals("URL mapbox should match", "https://www.mapbox.com/about/maps/", attribution.getUrl()); - assertEquals("Title mapbox should match", "Mapbox", attribution.getTitle()); - break; - case 1: - assertEquals("URL openstreetmap should match", "http://www.openstreetmap.org/about/", attribution.getUrl()); - assertEquals("Title openstreetmap should match", "OpenStreetMap", attribution.getTitle()); - break; - case 2: - assertEquals("URL improve map should match", "https://apps.mapbox.com/feedback/", attribution.getUrl()); - assertEquals("Title improve map should match", "Improve This Map", attribution.getTitle()); - break; - case 3: - assertEquals("URL digital globe should match", "https://www.digitalglobe.com/", attribution.getUrl()); - assertEquals("Title digital globe should match", "DigitalGlobe", attribution.getTitle()); - break; - } - counter++; - } - } - - @Test - public void testOutputWithoutCopyRightString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION) - .withCopyrightSign(false) - .withImproveMap(false) - .build(); - - assertEquals( - "Attribution string should match", - "© Mapbox / OpenStreetMap", - attributionParser.createAttributionString() - ); - } - - - @Test - public void testOutputWithCopyRightString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION) - .withImproveMap(false) - .build(); - - assertEquals( - "Attribution string should match", - "© Mapbox / © OpenStreetMap", - attributionParser.createAttributionString() - ); - } - - @Test - public void testOutputWithoutCopyRightWithoutMapboxString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION) - .withCopyrightSign(false) - .withImproveMap(false) - .withMapboxAttribution(false) - .build(); - - assertEquals( - "Attribution string should match", - "© OpenStreetMap", - attributionParser.createAttributionString() - ); - } - - @Test - public void testOutputWithCopyRightWithoutMapboxString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION) - .withImproveMap(false) - .withMapboxAttribution(false) - .build(); - - assertEquals( - "Attribution string should match", - "© OpenStreetMap", - attributionParser.createAttributionString() - ); - } - - @Test - public void testOutputSatelliteString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "") - .withImproveMap(false) - .withCopyrightSign(false) - .withMapboxAttribution(false) - .build(); - - assertEquals( - "Attribution string should match", - "© OpenStreetMap / DigitalGlobe", - attributionParser.createAttributionString() - ); - } - - @Test - public void testShortOpenStreetMapString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "") - .withImproveMap(false) - .withCopyrightSign(false) - .withMapboxAttribution(false) - .build(); - - assertEquals( - "Attribution string should match", - "© OSM / DigitalGlobe", - attributionParser.createAttributionString(true) - ); - } - - @Test - public void testShortOpenStreetMapWithoutCopyrightString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION, SATELLITE_ATTRIBUTION, "blabla", "") - .withImproveMap(false) - .withCopyrightSign(false) - .build(); - - assertEquals( - "Attribution string should match", - "© Mapbox / OSM / DigitalGlobe", - attributionParser.createAttributionString(true) - ); - } - - @Test - public void testWithImproveThisMapString() throws Exception { - AttributionParser attributionParser = new AttributionParser.Options(RuntimeEnvironment.application) - .withAttributionData(STREETS_ATTRIBUTION, "blabla", "") - .withImproveMap(true) - .withCopyrightSign(false) - .build(); - - assertEquals( - "Attribution string should match", - "© Mapbox / OSM / Improve This Map", - attributionParser.createAttributionString(true) - ); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java deleted file mode 100644 index dcf84c87b..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/camera/CameraPositionTest.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.mapbox.mapboxsdk.camera; - -import android.content.res.TypedArray; -import android.os.Parcel; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(RobolectricTestRunner.class) -public class CameraPositionTest { - - private static final double DELTA = 1e-15; - - @Test - public void testSanity() { - LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); - assertNotNull("cameraPosition should not be null", cameraPosition); - } - - @Test - public void testDefaultTypedArrayBuilder() { - TypedArray typedArray = null; - CameraPosition cameraPosition = new CameraPosition.Builder(typedArray).build(); - assertEquals("bearing should match", -1, cameraPosition.bearing, DELTA); - assertNull("latlng should be null", cameraPosition.target); - assertEquals("tilt should match", -1, cameraPosition.tilt, DELTA); - assertEquals("zoom should match", -1, cameraPosition.zoom, DELTA); - assertNull("padding should be null", cameraPosition.padding); - } - - @Test - public void testTypedArrayBuilder() { - float bearing = 180; - float zoom = 12; - float latitude = 10; - float longitude = 11; - float tilt = 44; - - TypedArray typedArray = mock(TypedArray.class); - when(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraBearing, 0.0f)).thenReturn(bearing); - when(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTargetLat, 0.0f)).thenReturn(latitude); - when(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTargetLng, 0.0f)).thenReturn(longitude); - when(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraZoom, 0.0f)).thenReturn(zoom); - when(typedArray.getFloat(R.styleable.mapbox_MapView_mapbox_cameraTilt, 0.0f)).thenReturn(tilt); - doNothing().when(typedArray).recycle(); - - CameraPosition cameraPosition = new CameraPosition.Builder(typedArray).build(); - assertEquals("bearing should match", bearing, cameraPosition.bearing, DELTA); - assertEquals("latlng should match", new LatLng(latitude, longitude), cameraPosition.target); - assertEquals("tilt should match", tilt, cameraPosition.tilt, DELTA); - assertEquals("zoom should match", zoom, cameraPosition.zoom, DELTA); - } - - @Test - public void testToString() { - LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); - assertEquals("toString should match", "Target: LatLng [latitude=1.0, longitude=2.0, altitude=0.0], Zoom:3.0, " - + "Bearing:5.0, Tilt:4.0, Padding:[0.0, 500.0, 0.0, 0.0]", cameraPosition.toString()); - } - - @Test - public void testHashcode() { - LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); - assertEquals("hashCode should match", -420915327, cameraPosition.hashCode()); - } - - @Test - public void testZoomUpdateBuilder() { - float zoomLevel = 5; - CameraPosition.Builder builder = new CameraPosition.Builder( - (CameraUpdateFactory.ZoomUpdate) CameraUpdateFactory.zoomTo(zoomLevel)); - assertEquals("zoom should match", zoomLevel, builder.build().zoom, 0); - } - - @Test - public void testEquals() { - LatLng latLng = new LatLng(1, 2); - CameraPosition cameraPosition = new CameraPosition(latLng, 3, 4, 5, new double[] {0, 500, 0, 0}); - CameraPosition cameraPositionBearing = new CameraPosition(latLng, 3, 4, 9, new double[] {0, 500, 0, 0}); - CameraPosition cameraPositionTilt = new CameraPosition(latLng, 3, 9, 5, new double[] {0, 500, 0, 0}); - CameraPosition cameraPositionZoom = new CameraPosition(latLng, 9, 4, 5, new double[] {0, 500, 0, 0}); - CameraPosition cameraPositionTarget = new CameraPosition(new LatLng(), 3, 4, 5, new double[] {0, 500, 0, 0}); - CameraPosition cameraPositionPadding = new CameraPosition(new LatLng(), 3, 4, 5, new double[] {0, 501, 0, 0}); - - assertEquals("cameraPosition should match itself", cameraPosition, cameraPosition); - assertNotEquals("cameraPosition should not match null", null, cameraPosition); - assertNotEquals("cameraPosition should not match object", new Object(), cameraPosition); - assertNotEquals("cameraPosition should not match for bearing", cameraPositionBearing, cameraPosition); - assertNotEquals("cameraPosition should not match for tilt", cameraPositionTilt, cameraPosition); - assertNotEquals("cameraPosition should not match for zoom", cameraPositionZoom, cameraPosition); - assertNotEquals("cameraPosition should not match for target", cameraPositionTarget, cameraPosition); - assertNotEquals("cameraPosition should not match for padding", cameraPositionPadding, cameraPosition); - } - - @Test - public void testParcelable() { - CameraPosition cameraPosition1 = new CameraPosition(new LatLng(1, 2), 3, 4, 5, new double[] {0, 500, 0, 0}); - Parcel parcel = Parcel.obtain(); - cameraPosition1.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - - CameraPosition cameraPosition2 = CameraPosition.CREATOR.createFromParcel(parcel); - assertEquals("Parcel should match original object", cameraPosition1, cameraPosition2); - } - - @Test - public void testParcelableNulls() { - CameraPosition cameraPosition1 = new CameraPosition(null, 3, 4, 5, null); - Parcel parcel = Parcel.obtain(); - cameraPosition1.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - - CameraPosition cameraPosition2 = CameraPosition.CREATOR.createFromParcel(parcel); - assertEquals("Parcel should match original object", cameraPosition1, cameraPosition2); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java deleted file mode 100644 index cb654aa55..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/constants/AppConstant.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.mapbox.mapboxsdk.constants; - -public class AppConstant { - - public static final int STYLE_VERSION = 9; -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java deleted file mode 100644 index 6c44c29e3..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngBoundsTest.java +++ /dev/null @@ -1,813 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcelable; -import com.mapbox.mapboxsdk.constants.GeometryConstants; -import com.mapbox.mapboxsdk.exceptions.InvalidLatLngBoundsException; -import com.mapbox.mapboxsdk.utils.MockParcel; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; - -import java.util.ArrayList; -import java.util.List; - -import static junit.framework.Assert.assertNotNull; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -public class LatLngBoundsTest { - - private static final double DELTA = 1e-15; - - private LatLngBounds latLngBounds; - private static final LatLng LAT_LNG_NULL_ISLAND = new LatLng(0, 0); - private static final LatLng LAT_LNG_NOT_NULL_ISLAND = new LatLng(2, 2); - - @Before - public void beforeTest() { - latLngBounds = new LatLngBounds.Builder() - .include(LAT_LNG_NULL_ISLAND) - .include(LAT_LNG_NOT_NULL_ISLAND) - .build(); - } - - @Test - public void testSanity() { - LatLngBounds.Builder latLngBoundsBuilder = new LatLngBounds.Builder(); - latLngBoundsBuilder.include(LAT_LNG_NULL_ISLAND).include(LAT_LNG_NOT_NULL_ISLAND); - assertNotNull("latLng should not be null", latLngBoundsBuilder.build()); - } - - @Test(expected = InvalidLatLngBoundsException.class) - public void noLatLngs() { - new LatLngBounds.Builder().build(); - } - - @Test(expected = InvalidLatLngBoundsException.class) - public void oneLatLngs() { - new LatLngBounds.Builder().include(LAT_LNG_NULL_ISLAND).build(); - } - - @Test - public void latitiudeSpan() { - assertEquals("Span should be the same", 2, latLngBounds.getLatitudeSpan(), DELTA); - } - - @Test - public void longitudeSpan() { - assertEquals("Span should be the same", 2, latLngBounds.getLongitudeSpan(), DELTA); - } - - @Test - public void coordinateSpan() { - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be the same", new LatLngSpan(2, 2), latLngSpan); - } - - @Test - public void dateLineSpanBuilder1() { - latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, -170)) - .include(new LatLng(-10, 170)) - .build(); - - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be the same", new LatLngSpan(20, 340), - latLngSpan); - } - - @Test - public void dateLineSpanBuilder2() { - latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(-10, -170)) - .include(new LatLng(10, 170)) - .build(); - - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be the same", new LatLngSpan(20, 340), - latLngSpan); - } - - @Test - public void dateLineSpanFrom1() { - latLngBounds = LatLngBounds.from(10, -170, -10, -190); - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be the same", new LatLngSpan(20, 20), - latLngSpan); - } - - @Test - public void dateLineSpanFrom2() { - latLngBounds = LatLngBounds.from(10, 170, -10, -170); - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be the same", new LatLngSpan(20, 340), - latLngSpan); - } - - @Test - public void zeroLongitudeSpan() { - latLngBounds = LatLngBounds.from(10, 10, -10, 10); - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be shortest distance", new LatLngSpan(20, 0), - latLngSpan); - } - - @Test - public void nearDateLineCenter1() { - latLngBounds = LatLngBounds.from(10, -175, -10, -195); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(0, -185), center); - } - - @Test - public void nearDateLineCenter2() { - latLngBounds = LatLngBounds.from(10, 195, -10, 175); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(0, 185), center); - } - - @Test - public void nearDateLineCenter3() { - latLngBounds = LatLngBounds.from(10, -170, -10, -190); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(0, -180), center); - } - - @Test - public void nearDateLineCenter4() { - latLngBounds = LatLngBounds.from(10, 180, -10, 0); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(0, 90), center); - } - - @Test - public void nearDateLineCenter5() { - latLngBounds = LatLngBounds.from(10, 180, -10, 0); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(0, 90), center); - } - - @Test - public void centerForBoundsWithSameLongitude() { - latLngBounds = LatLngBounds.from(10, 10, -10, 10); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(0, 10), center); - } - - @Test - public void centerForBoundsWithSameLatitude() { - latLngBounds = LatLngBounds.from(10, 10, 10, -10); - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(10, 0), center); - } - - @Test - public void center() { - LatLng center = latLngBounds.getCenter(); - assertEquals("Center should match", new LatLng(1, 1), center); - } - - @Test - public void notEmptySpan() { - latLngBounds = new LatLngBounds.Builder() - .include(LAT_LNG_NOT_NULL_ISLAND) - .include(LAT_LNG_NULL_ISLAND) - .build(); - assertFalse("Should not be empty", latLngBounds.isEmptySpan()); - } - - @Test - public void includeSameLatLngs() { - latLngBounds = new LatLngBounds.Builder() - .include(LAT_LNG_NOT_NULL_ISLAND) - .include(LAT_LNG_NOT_NULL_ISLAND) - .include(LAT_LNG_NULL_ISLAND) - .include(LAT_LNG_NULL_ISLAND) - .build(); - assertEquals(latLngBounds.getNorthEast(), LAT_LNG_NOT_NULL_ISLAND); - assertEquals(latLngBounds.getSouthWest(), LAT_LNG_NULL_ISLAND); - } - - @Test - public void toLatLngs() { - latLngBounds = new LatLngBounds.Builder() - .include(LAT_LNG_NOT_NULL_ISLAND) - .include(LAT_LNG_NULL_ISLAND) - .build(); - - assertArrayEquals("LatLngs should match", - new LatLng[] {LAT_LNG_NOT_NULL_ISLAND, LAT_LNG_NULL_ISLAND}, - latLngBounds.toLatLngs()); - } - - @Test - public void include() { - assertTrue("LatLng should be included", latLngBounds.contains(new LatLng(1, 1))); - } - - @Test - public void includes() { - List points = new ArrayList<>(); - points.add(LAT_LNG_NULL_ISLAND); - points.add(LAT_LNG_NOT_NULL_ISLAND); - - LatLngBounds latLngBounds1 = new LatLngBounds.Builder() - .includes(points) - .build(); - - LatLngBounds latLngBounds2 = new LatLngBounds.Builder() - .include(LAT_LNG_NULL_ISLAND) - .include(LAT_LNG_NOT_NULL_ISLAND) - .build(); - - assertEquals("LatLngBounds should match", latLngBounds1, latLngBounds2); - } - - @Test - public void includesOrderDoesNotMatter() { - LatLngBounds sameLongitudeFirst = new LatLngBounds.Builder() - .include(new LatLng(50, 10)) // southWest - .include(new LatLng(60, 10)) - .include(new LatLng(60, 20)) // northEast - .include(new LatLng(50, 20)) - .include(new LatLng(50, 10)) // southWest again - .build(); - - LatLngBounds sameLatitudeFirst = new LatLngBounds.Builder() - .include(new LatLng(50, 20)) - .include(new LatLng(50, 10)) // southWest - .include(new LatLng(60, 10)) - .include(new LatLng(60, 20)) // northEast - .include(new LatLng(50, 20)) - .build(); - - assertEquals(sameLatitudeFirst, sameLongitudeFirst); - } - - @Test - public void includesOverDateline1() { - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, -170)) - .include(new LatLng(-10, -175)) - .include(new LatLng(0, -190)) - .build(); - - LatLngSpan latLngSpan = latLngBounds.getSpan(); - assertEquals("LatLngSpan should be the same", - new LatLngSpan(20, 20), latLngSpan); - } - - @Test - public void includesOverDateline2() { - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, 170)) - .include(new LatLng(-10, 175)) - .include(new LatLng(0, 190)) - .build(); - - assertEquals("LatLngSpan should be the same", - new LatLngSpan(20, 20), latLngBounds.getSpan()); - } - - @Test - public void includesOverDateline3() { - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, -190)) - .include(new LatLng(-10, -170)) - .include(new LatLng(0, -180)) - .include(new LatLng(5, -180)) - .build(); - - assertEquals("LatLngSpan should be the same", - new LatLngSpan(20, 20), latLngBounds.getSpan()); - } - - @Test - public void containsNot() { - assertFalse("LatLng should not be included", - latLngBounds.contains(new LatLng(3, 1))); - } - - @Test - public void containsBoundsInWorld() { - assertTrue("LatLngBounds should be contained in the world", - LatLngBounds.world().contains(latLngBounds)); - } - - @Test - public void worldSpan() { - assertEquals("LatLngBounds world span should be 180, 360", - GeometryConstants.LATITUDE_SPAN, LatLngBounds.world().getLatitudeSpan(), DELTA); - assertEquals("LatLngBounds world span should be 180, 360", - GeometryConstants.LONGITUDE_SPAN, LatLngBounds.world().getLongitudeSpan(), DELTA); - } - - @Test - public void emptySpan() { - LatLngBounds latLngBounds = LatLngBounds.from( - GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE, - GeometryConstants.MIN_LATITUDE, GeometryConstants.MAX_LONGITUDE); - assertTrue("LatLngBounds empty span", latLngBounds.isEmptySpan()); - } - - @Test - public void containsBounds() { - LatLngBounds inner = new LatLngBounds.Builder() - .include(new LatLng(-5, -5)) - .include(new LatLng(5, 5)) - .build(); - LatLngBounds outer = new LatLngBounds.Builder() - .include(new LatLng(-10, -10)) - .include(new LatLng(10, 10)) - .build(); - assertTrue(outer.contains(inner)); - assertFalse(inner.contains(outer)); - } - - @Test - public void testHashCode() { - assertEquals(2147483647, latLngBounds.hashCode(), -1946419200); - } - - @Test - public void equality() { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(LAT_LNG_NULL_ISLAND) - .include(LAT_LNG_NOT_NULL_ISLAND) - .build(); - assertEquals("equality should match", this.latLngBounds, latLngBounds); - assertEquals("not equal to a different object type", - this.latLngBounds.equals(LAT_LNG_NOT_NULL_ISLAND), false); - } - - @Test - public void testToString() { - assertEquals(latLngBounds.toString(), "N:2.0; E:2.0; S:0.0; W:0.0"); - } - - @Test - public void intersect() { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(1, 1)) - .include(LAT_LNG_NULL_ISLAND) - .build(); - assertEquals("intersect should match", latLngBounds, - latLngBounds.intersect(this.latLngBounds.getLatNorth(), - this.latLngBounds.getLonEast(), - this.latLngBounds.getLatSouth(), - this.latLngBounds.getLonWest())); - } - - @Test - public void intersectNot() { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, 10)) - .include(new LatLng(9, 8)) - .build(); - assertNull(latLngBounds.intersect(this.latLngBounds)); - } - - @Test - public void intersectNorthCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds intersectLatLngBounds = - LatLngBounds.from(10, 10, 0, 0) - .intersect(200, 200, 0, 0); - } - - @Test - public void intersectSouthCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds intersectLatLngBounds = - LatLngBounds.from(0, 0, -10, -10) - .intersect(0, 0, -200, -200); - } - - @Test - public void intersectSouthLessThanNorthCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latNorth cannot be less than latSouth"); - LatLngBounds intersectLatLngBounds = - LatLngBounds.from(10, 10, 0, 0) - .intersect(0, 200, 20, 0); - } - - @Test - public void intersectEastLessThanWestCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("lonEast cannot be less than lonWest"); - LatLngBounds intersectLatLngBounds = - LatLngBounds.from(10, -10, 0, 0) - .intersect(0, 200, 20, 0); - } - - public void intersectEastDoesNotWrapCheck() { - - LatLngBounds latLngBounds1 = LatLngBounds.from(10, 210, 0, 0); - LatLngBounds latLngBounds2 = LatLngBounds.from(90, 200, 0, 0); - - LatLngBounds intersectLatLngBounds = LatLngBounds.from(10, 200, 0, 0); - - assertEquals(latLngBounds1.intersect(latLngBounds2), intersectLatLngBounds); - assertEquals(latLngBounds2.intersect(latLngBounds1), intersectLatLngBounds); - } - - @Test - public void intersectDoesNotWestWrapCheck() { - LatLngBounds latLngBounds1 = LatLngBounds.from(0, 0, -10, -210); - LatLngBounds latLngBounds2 = LatLngBounds.from(0, 0, -90, -200); - - LatLngBounds intersectLatLngBounds = LatLngBounds.from(0, 0, -10, -200); - - assertEquals(latLngBounds1.intersect(latLngBounds2), intersectLatLngBounds); - assertEquals(latLngBounds2.intersect(latLngBounds1), intersectLatLngBounds); - } - - @Test - public void innerUnion() { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(1, 1)) - .include(LAT_LNG_NULL_ISLAND) - .build(); - assertEquals("union should match", latLngBounds, latLngBounds.intersect(this.latLngBounds)); - } - - @Test - public void outerUnion() { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, 10)) - .include(new LatLng(9, 8)) - .build(); - assertEquals("outer union should match", - latLngBounds.union(this.latLngBounds), - new LatLngBounds.Builder() - .include(new LatLng(10, 10)) - .include(LAT_LNG_NULL_ISLAND) - .build()); - } - - @Test - public void unionOverDateLine() { - LatLngBounds latLngBounds1 = new LatLngBounds.Builder() - .include(new LatLng(10, 170)) - .include(new LatLng(0, 160)) - .build(); - - LatLngBounds latLngBounds2 = new LatLngBounds.Builder() - .include(new LatLng(0, 190)) - .include(new LatLng(-10, 200)) - .build(); - - LatLngBounds union1 = latLngBounds1.union(latLngBounds2); - LatLngBounds union2 = latLngBounds2.union(latLngBounds1); - - assertEquals(union1, - new LatLngBounds.Builder() - .include(new LatLng(10, 160)) - .include(new LatLng(-10, 200)) - .build()); - - assertEquals(union1, union2); - } - - @Test - public void unionOverDateLine2() { - LatLngBounds latLngBounds1 = new LatLngBounds.Builder() - .include(new LatLng(10, 170)) - .include(new LatLng(0, 160)) - .build(); - - LatLngBounds latLngBounds2 = new LatLngBounds.Builder() - .include(new LatLng(0, 165)) - .include(new LatLng(-10, 200)) - .build(); - - LatLngBounds union1 = latLngBounds1.union(latLngBounds2); - LatLngBounds union2 = latLngBounds2.union(latLngBounds1); - - assertEquals(union1, - new LatLngBounds.Builder() - .include(new LatLng(10, 160)) - .include(new LatLng(-10, 200)) - .build()); - - assertEquals(union1, union2); - } - - @Test - public void unionOverDateLine3() { - LatLngBounds latLngBounds1 = new LatLngBounds.Builder() - .include(new LatLng(10, 195)) - .include(new LatLng(0, 160)) - .build(); - - LatLngBounds latLngBounds2 = new LatLngBounds.Builder() - .include(new LatLng(0, 190)) - .include(new LatLng(-10, 200)) - .build(); - - LatLngBounds union1 = latLngBounds1.union(latLngBounds2); - LatLngBounds union2 = latLngBounds2.union(latLngBounds1); - - assertEquals(union1, - new LatLngBounds.Builder() - .include(new LatLng(10, 160)) - .include(new LatLng(-10, 200)) - .build()); - - assertEquals(union1, union2); - } - - @Test - public void unionOverDateLine4() { - LatLngBounds latLngBounds1 = new LatLngBounds.Builder() - .include(new LatLng(10, -160)) - .include(new LatLng(0, -200)) - .build(); - - LatLngBounds latLngBounds2 = new LatLngBounds.Builder() - .include(new LatLng(0, -170)) - .include(new LatLng(-10, -175)) - .build(); - - LatLngBounds union1 = latLngBounds1.union(latLngBounds2); - LatLngBounds union2 = latLngBounds2.union(latLngBounds1); - - assertEquals(union1, - new LatLngBounds.Builder() - .include(new LatLng(10, -200)) - .include(new LatLng(-10, -160)) - .build()); - - assertEquals(union1, union2); - } - - @Test - public void unionOverDateLine5() { - LatLngBounds latLngBounds1 = new LatLngBounds.Builder() - .include(new LatLng(10, 200)) - .include(new LatLng(0, 160)) - .build(); - - LatLngBounds latLngBounds2 = new LatLngBounds.Builder() - .include(new LatLng(0, 170)) - .include(new LatLng(-10, 175)) - .build(); - - LatLngBounds union1 = latLngBounds1.union(latLngBounds2); - LatLngBounds union2 = latLngBounds2.union(latLngBounds1); - - assertEquals(union1, - new LatLngBounds.Builder() - .include(new LatLng(10, 160)) - .include(new LatLng(-10, 200)) - .build()); - - assertEquals(union1, union2); - } - - @Test - public void unionOverDateLineReturnLongerThanWorldLonSpan() { - LatLngBounds latLngBounds1 = LatLngBounds.from(10, 200, -10, -10); - LatLngBounds latLngBounds2 = LatLngBounds.from(10, 10, -10, -200); - - LatLngBounds union1 = latLngBounds1.union(latLngBounds2); - LatLngBounds union2 = latLngBounds2.union(latLngBounds1); - - assertEquals(union1, union2); - assertEquals(union1, LatLngBounds.from(10, 200, -10, -200)); - } - - @Test - public void unionNorthCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds unionLatLngBounds = - LatLngBounds.from(10, 10, 0, 0) - .union(200, 200, 0, 0); - } - - @Test - public void unionSouthCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds unionLatLngBounds = - LatLngBounds.from(0, 0, -10, -10) - .union(0, 0, -200, -200); - } - - @Test - public void unionSouthLessThanNorthCheck() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latNorth cannot be less than latSouth"); - - LatLngBounds unionLatLngBounds = - LatLngBounds.from(10, 10, 0, 0) - .union(0, 200, 20, 0); - } - - - @Test - public void unionEastDoesNotWrapCheck() { - - LatLngBounds latLngBounds1 = LatLngBounds.from(10, 10, 0, 0); - LatLngBounds latLngBounds2 = LatLngBounds.from(90, 200, 0, 0); - LatLngBounds unionLatLngBounds = LatLngBounds.from(90, 200, 0, 0); - - assertEquals(latLngBounds1.union(latLngBounds2), unionLatLngBounds); - assertEquals(latLngBounds2.union(latLngBounds1), unionLatLngBounds); - } - - @Test - public void unionWestDoesNotWrapCheck() { - LatLngBounds latLngBounds1 = LatLngBounds.from(0, 0, -10, -10); - LatLngBounds latLngBounds2 = LatLngBounds.from(0, 0, -90, -200); - - LatLngBounds unionLatLngBounds = LatLngBounds.from(0, 0, -90, -200); - - assertEquals(latLngBounds1.union(latLngBounds2), unionLatLngBounds); - assertEquals(latLngBounds2.union(latLngBounds1), unionLatLngBounds); - } - - @Test - public void northWest() { - double minLat = 5; - double minLon = 6; - double maxLat = 20; - double maxLon = 21; - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(minLat, minLon)) - .include(new LatLng(maxLat, maxLon)) - .build(); - - assertEquals("NorthWest should match", latLngBounds.getNorthWest(), new LatLng(maxLat, minLon)); - } - - @Test - public void southWest() { - double minLat = 5; - double minLon = 6; - double maxLat = 20; - double maxLon = 21; - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(minLat, minLon)) - .include(new LatLng(maxLat, maxLon)) - .build(); - - assertEquals("SouthWest should match", latLngBounds.getSouthWest(), new LatLng(minLat, minLon)); - } - - @Test - public void northEast() { - double minLat = 5; - double minLon = 6; - double maxLat = 20; - double maxLon = 21; - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(minLat, minLon)) - .include(new LatLng(maxLat, maxLon)) - .build(); - - assertEquals("NorthEast should match", latLngBounds.getNorthEast(), new LatLng(maxLat, maxLon)); - } - - @Test - public void southEast() { - double minLat = 5; - double minLon = 6; - double maxLat = 20; - double maxLon = 21; - - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(minLat, minLon)) - .include(new LatLng(maxLat, maxLon)) - .build(); - - assertEquals("SouthEast should match", latLngBounds.getSouthEast(), new LatLng(minLat, maxLon)); - } - - @Test - public void testParcelable() { - LatLngBounds latLngBounds = new LatLngBounds.Builder() - .include(new LatLng(10, 10)) - .include(new LatLng(9, 8)) - .build(); - Parcelable parcel = MockParcel.obtain(latLngBounds); - assertEquals("Parcel should match original object", parcel, latLngBounds); - } - - @Test - public void fromTileID() { - LatLngBounds bounds = LatLngBounds.from(0, 0, 0); - assertEquals(GeometryConstants.MIN_WRAP_LONGITUDE, bounds.getLonWest(), DELTA); - assertEquals(GeometryConstants.MIN_MERCATOR_LATITUDE, bounds.getLatSouth(), DELTA); - assertEquals(GeometryConstants.MAX_WRAP_LONGITUDE, bounds.getLonEast(), DELTA); - assertEquals(GeometryConstants.MAX_MERCATOR_LATITUDE, bounds.getLatNorth(), DELTA); - - bounds = LatLngBounds.from(10, 288, 385); - assertEquals(-78.75, bounds.getLonWest(), DELTA); - assertEquals(40.446947059600497, bounds.getLatSouth(), DELTA); - assertEquals(-78.3984375, bounds.getLonEast(), DELTA); - assertEquals(40.713955826286039, bounds.getLatNorth(), DELTA); - - } - - @Rule - public final ExpectedException exception = ExpectedException.none(); - - @Test - public void testConstructorChecksNorthLatitudeNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must not be NaN"); - LatLngBounds.from(Double.NaN, 0, -20, -20); - } - - @Test - public void testConstructorChecksEastLongitudeNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be NaN"); - LatLngBounds.from(0, Double.NaN, -20, -20); - } - - @Test - public void testConstructorChecksNorthLatitudeGreaterThan90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(95, 0, -20, -20); - } - - @Test - public void testConstructorChecksNorthLatitudeLessThanThanNegative90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(-95, 0, -20, -20); - } - - @Test - public void testConstructorChecksEastLongitudeInfinity() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be infinite"); - LatLngBounds.from(0, Double.POSITIVE_INFINITY, -20, -20); - } - - @Test - public void testConstructorChecksSouthLatitudeNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must not be NaN"); - LatLngBounds.from(20, 20, Double.NaN, 0); - } - - @Test - public void testConstructorChecksWesttLongitudeNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be NaN"); - LatLngBounds.from(20, 20, 0, Double.NaN); - } - - @Test - public void testConstructorChecksSouthLatitudeGreaterThan90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20, 95, 0); - } - - @Test - public void testConstructorChecksSouthLatitudeLessThanThanNegative90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - LatLngBounds.from(20, 20, -95, 0); - } - - @Test - public void testConstructorChecksWestLongitudeInfinity() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be infinite"); - LatLngBounds.from(20, 20, 0, Double.POSITIVE_INFINITY); - } - - @Test - public void testConstructorCheckLatSouthGreaterLatNorth() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latNorth cannot be less than latSouth"); - LatLngBounds.from(0, 20, 20, 0); - } - - @Test - public void testConstructorCheckLonWestGreaterLonEast() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("lonEast cannot be less than lonWest"); - LatLngBounds.from(20, 0, 0, 20); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java deleted file mode 100644 index 12297247c..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngSpanTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcelable; - -import com.mapbox.mapboxsdk.utils.MockParcel; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class LatLngSpanTest { - - private static final double DELTA = 1e-15; - private static final LatLng LAT_LNG_NULL_ISLAND = new LatLng(0, 0); - - @Test - public void testSanity() { - LatLngSpan latLngSpan = new LatLngSpan(0.0, 0.0); - assertNotNull("latLngSpan should not be null", latLngSpan); - } - - @Test - public void testEquality() { - LatLngSpan latLngSpan = new LatLngSpan(0.0, 0.0); - assertEquals("latLngSpan is not equal to a LatLng", latLngSpan.equals(LAT_LNG_NULL_ISLAND), false); - } - - @Test - public void testLatitudeConstructor() { - double latitude = 1.23; - LatLngSpan latLngSpan = new LatLngSpan(latitude, 0.0); - assertEquals("latitude in constructor", latLngSpan.getLatitudeSpan(), latitude, DELTA); - } - - @Test - public void testLongitudeConstructor() { - double longitude = 1.23; - LatLngSpan latLngSpan = new LatLngSpan(0.0, longitude); - assertEquals("latitude in constructor", latLngSpan.getLongitudeSpan(), longitude, DELTA); - } - - @Test - public void testLatitudeMethod() { - double latitude = 1.23; - LatLngSpan latLngSpan = new LatLngSpan(0.0, 0.0); - latLngSpan.setLatitudeSpan(latitude); - assertEquals("latitude in constructor", latLngSpan.getLatitudeSpan(), latitude, DELTA); - } - - @Test - public void testLongitudeMethod() { - double longitude = 1.23; - LatLngSpan latLngSpan = new LatLngSpan(0.0, 0.0); - latLngSpan.setLongitudeSpan(longitude); - assertEquals("latitude in constructor", latLngSpan.getLongitudeSpan(), longitude, DELTA); - } - - @Test - public void testParcelable() { - LatLngSpan object = new LatLngSpan(1, 2); - Parcelable parcel = MockParcel.obtain(object); - assertEquals("parcel should match initial object", object, parcel); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java deleted file mode 100644 index 862c56a52..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/LatLngTest.java +++ /dev/null @@ -1,286 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.location.Location; -import android.os.Parcelable; - -import com.mapbox.mapboxsdk.utils.MockParcel; - -import org.junit.Test; -import org.junit.Rule; -import org.junit.rules.ExpectedException; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class LatLngTest { - - private static final double DELTA = 1e-15; - - @Test - public void testSanity() { - LatLng latLng = new LatLng(0.0, 0.0); - assertNotNull("latLng should not be null", latLng); - } - - @Test - public void testLatitudeEmptyConstructor() { - LatLng latLng = new LatLng(); - assertEquals("latitude default value", latLng.getLatitude(), 0, DELTA); - } - - @Test - public void testLongitudeEmptyConstructor() { - LatLng latLng = new LatLng(); - assertEquals("longitude default value", latLng.getLongitude(), 0, DELTA); - } - - @Test - public void testAltitudeEmptyConstructor() { - LatLng latLng1 = new LatLng(); - assertEquals("altitude default value", latLng1.getAltitude(), 0.0, DELTA); - } - - @Test - public void testLatitudeConstructor() { - double latitude = 1.2; - LatLng latLng = new LatLng(latitude, 3.4); - assertEquals("latitude should match", latLng.getLatitude(), latitude, DELTA); - } - - @Test - public void testLongitudeConstructor() { - double longitude = 3.4; - LatLng latLng = new LatLng(1.2, longitude); - assertEquals("longitude should match", latLng.getLongitude(), longitude, DELTA); - } - - @Test - public void testAltitudeConstructor() { - LatLng latLng1 = new LatLng(1.2, 3.4); - assertEquals("altitude default value", latLng1.getAltitude(), 0.0, DELTA); - - double altitude = 5.6; - LatLng latLng2 = new LatLng(1.2, 3.4, altitude); - assertEquals("altitude default value", latLng2.getAltitude(), altitude, DELTA); - } - - @Test - public void testLatitudeSetter() { - LatLng latLng = new LatLng(1.2, 3.4); - latLng.setLatitude(3); - assertEquals("latitude should match", 3, latLng.getLatitude(), DELTA); - } - - @Test - public void testLongitudeSetter() { - LatLng latLng = new LatLng(1.2, 3.4); - latLng.setLongitude(3); - assertEquals("longitude should match", 3, latLng.getLongitude(), DELTA); - } - - @Rule - public final ExpectedException exception = ExpectedException.none(); - - @Test - public void testConstructorChecksLatitudeNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must not be NaN"); - new LatLng(Double.NaN, 0); - } - - @Test - public void testConstructorChecksLongitudeNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be NaN"); - new LatLng(0, Double.NaN); - } - - @Test - public void testConstructorChecksLatitudeGreaterThan90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - new LatLng(95, 0); - } - - @Test - public void testConstructorChecksLatitudeLessThanThanNegative90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - new LatLng(-95, 0); - } - - @Test - public void testConstructorChecksLongitudeInfinity() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be infinite"); - new LatLng(0, Double.POSITIVE_INFINITY); - } - - @Test - public void testLatitudeSetterChecksNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must not be NaN"); - new LatLng().setLatitude(Double.NaN); - } - - @Test - public void testLongitudeSetterChecksNaN() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be NaN"); - new LatLng().setLongitude(Double.NaN); - } - - @Test - public void testLatitudeSetterChecksGreaterThan90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - new LatLng().setLatitude(95); - } - - @Test - public void testLatitudeSetterChecksLessThanThanNegative90() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("latitude must be between -90 and 90"); - new LatLng().setLatitude(-95); - } - - @Test - public void testLongitudeSetterChecksInfinity() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("longitude must not be infinite"); - new LatLng().setLongitude(Double.NEGATIVE_INFINITY); - } - - @Test - public void testAltitudeSetter() { - LatLng latLng = new LatLng(1.2, 3.4); - latLng.setAltitude(3); - assertEquals("altitude should match", 3, latLng.getAltitude(), DELTA); - } - - @Test - public void testLatLngConstructor() { - LatLng latLng1 = new LatLng(1.2, 3.4); - LatLng latLng2 = new LatLng(latLng1); - assertEquals("latLng should match", latLng1, latLng2); - } - - @Test - public void testDistanceTo() { - LatLng latLng1 = new LatLng(0.0, 0.0); - LatLng latLng2 = new LatLng(1.0, 1.0); - assertEquals("distances should match", - latLng1.distanceTo(latLng2), - 157298.7453847275, DELTA); - } - - @Test - public void testDistanceToSamePoint() { - LatLng latLng1 = new LatLng(40.71199035644531, -74.0081); - LatLng latLng2 = new LatLng(40.71199035644531, -74.0081); - double distance = latLng1.distanceTo(latLng2); - assertEquals("distance should match", 0.0, distance, DELTA); - } - - // Regression test for #14216 - @Test - public void testDistanceToClosePointNotNaN() { - LatLng latLng = new LatLng(40.00599, -105.29261); - LatLng other = new LatLng(40.005990000000025, -105.29260999999997); - double distance = latLng.distanceTo(other); - assertNotEquals(distance, Double.NaN); - } - - @Test - public void testLocationProvider() { - double latitude = 1.2; - double longitude = 3.4; - double altitude = 5.6; - - // Mock the location class - Location locationMocked = mock(Location.class); - when(locationMocked.getLatitude()).thenReturn(latitude); - when(locationMocked.getLongitude()).thenReturn(longitude); - when(locationMocked.getAltitude()).thenReturn(altitude); - - // Test the constructor - LatLng latLng = new LatLng(locationMocked); - assertEquals("latitude should match", latLng.getLatitude(), latitude, DELTA); - assertEquals("longitude should match", latLng.getLongitude(), longitude, DELTA); - assertEquals("altitude should match", latLng.getAltitude(), altitude, DELTA); - } - - @Test - public void testHashCode() { - double latitude = 1.2; - double longitude = 3.4; - double altitude = 5.6; - LatLng latLng = new LatLng(latitude, longitude, altitude); - assertEquals("hash code should match", latLng.hashCode(), -151519232); - } - - @Test - public void testToString() { - double latitude = 1.2; - double longitude = 3.4; - double altitude = 5.6; - LatLng latLng = new LatLng(latitude, longitude, altitude); - assertEquals("string should match", - latLng.toString(), - "LatLng [latitude=1.2, longitude=3.4, altitude=5.6]"); - } - - @Test - public void testEqualsOther() { - double latitude = 1.2; - double longitude = 3.4; - double altitude = 5.6; - LatLng latLng1 = new LatLng(latitude, longitude, altitude); - LatLng latLng2 = new LatLng(latitude, longitude, altitude); - assertEquals("LatLng should match", latLng1, latLng2); - } - - @Test - public void testEqualsItself() { - LatLng latLng = new LatLng(1, 2, 3); - assertEquals("LatLng should match", latLng, latLng); - } - - @Test - public void testNotEquals() { - LatLng latLng = new LatLng(1, 2); - assertNotEquals("LatLng should match", latLng, new Object()); - } - - @Test - public void testParcelable() { - LatLng latLng = new LatLng(45.0, -185.0); - Parcelable parcel = MockParcel.obtain(latLng); - assertEquals("parcel should match initial object", latLng, parcel); - } - - @Test - public void testWrapped() { - LatLng originalLatLng = new LatLng(45.0, -185.0); - LatLng newLatlng = originalLatLng.wrap(); - assertNotSame(" new wrapped LatLng is created", originalLatLng, newLatlng); - assertEquals("longitude wrapped value", originalLatLng.getLongitude(), -185.0, DELTA); - assertEquals("longitude wrapped value", newLatlng.getLongitude(), 175.0, DELTA); - - newLatlng = new LatLng(45.0, 180.0).wrap(); - assertEquals("longitude wrapped max value", newLatlng.getLongitude(), 180.0, DELTA); - - newLatlng = new LatLng(45.0, -180.0).wrap(); - assertEquals("longitude wrapped min value", newLatlng.getLongitude(), -180.0, DELTA); - } - - @Test - public void testUnnecessaryWrapped() { - LatLng latLng = new LatLng(45.0, 50.0).wrap(); - assertEquals("longitude wrapped value", latLng.getLongitude(), 50.0, DELTA); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java deleted file mode 100644 index 00fd125a1..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/ProjectedMetersTest.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcelable; - -import com.mapbox.mapboxsdk.utils.MockParcel; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class ProjectedMetersTest { - - private static final LatLng LAT_LNG_NULL_ISLAND = new LatLng(0, 0); - - @Test - public void testSanity() { - ProjectedMeters projectedMeters = new ProjectedMeters(0.0, 0.0); - assertNotNull("projectedMeters should not be null", projectedMeters); - } - - @Test - public void testEquality() { - ProjectedMeters projectedMeters = new ProjectedMeters(0.0, 0.0); - assertEquals("projectedMeters is not equal to a LatLng", projectedMeters.equals(LAT_LNG_NULL_ISLAND), false); - assertEquals("projectedMeters is equal to itself", projectedMeters.equals(projectedMeters), true); - } - - @Test - public void testNorthing() { - ProjectedMeters projectedMeters = new ProjectedMeters(1.0, 0.0); - assertEquals("northing should be 1", 1, projectedMeters.getNorthing(), 0); - } - - @Test - public void testEasting() { - ProjectedMeters projectedMeters = new ProjectedMeters(0.0, 1.0); - assertEquals("easting should be 1", 1, projectedMeters.getEasting(), 0); - } - - @Test - public void testConstructor() { - ProjectedMeters projectedMeters1 = new ProjectedMeters(1, 2); - ProjectedMeters projectedMeters2 = new ProjectedMeters(projectedMeters1); - assertEquals("projectedmeters should match", projectedMeters1, projectedMeters2); - } - - @Test - public void testHashcode() { - ProjectedMeters meters = new ProjectedMeters(1, 2); - assertEquals("hashcode should match", -1048576, meters.hashCode()); - } - - @Test - public void testToString() { - ProjectedMeters meters = new ProjectedMeters(1, 1); - assertEquals("toString should match", "ProjectedMeters [northing=1.0, easting=1.0]", meters.toString()); - } - - @Test - public void testParcelable() { - ProjectedMeters meters = new ProjectedMeters(1, 1); - Parcelable parcel = MockParcel.obtain(meters); - assertEquals("parcel should match initial object", meters, parcel); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java deleted file mode 100644 index 12b779de5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/geometry/VisibleRegionTest.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.mapbox.mapboxsdk.geometry; - -import android.os.Parcelable; - -import com.mapbox.mapboxsdk.utils.MockParcel; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -public class VisibleRegionTest { - - private static final LatLng FAR_LEFT = new LatLng(52, -12); - private static final LatLng NEAR_LEFT = new LatLng(34, -12); - private static final LatLng FAR_RIGHT = new LatLng(52, 26); - private static final LatLng NEAR_RIGHT = new LatLng(34, 26); - private static final LatLngBounds BOUNDS = - new LatLngBounds.Builder().include(FAR_LEFT).include(FAR_RIGHT).include(NEAR_LEFT).include(NEAR_RIGHT).build(); - - @Test - public void testSanity() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertNotNull("region should not be null", region); - } - - @Test - public void testEquality() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("visibleRegion is not equal to a LatLng", region.equals(FAR_LEFT), false); - assertEquals("visibleRegion is equal to itself", region.equals(region), true); - } - - @Test - public void testFarLeftConstructor() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("LatLng should match", region.farLeft, FAR_LEFT); - } - - @Test - public void testNearLeftConstructor() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("LatLng should match", region.nearLeft, NEAR_LEFT); - } - - @Test - public void testFarRightConstructor() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("LatLng should match", region.farRight, FAR_RIGHT); - } - - @Test - public void testNearRightConstructor() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("LatLng should match", region.nearRight, NEAR_RIGHT); - } - - @Test - public void testLatLngBoundsConstructor() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("LatLngBounds should match", region.latLngBounds, BOUNDS); - } - - @Test - public void testEquals() { - VisibleRegion regionLeft = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - VisibleRegion regionRight = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("VisibleRegions should match", regionLeft, regionRight); - } - - @Test - public void testHashcode() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("hashcode should match", -923534102, region.hashCode()); - } - - @Test - public void testToString() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - assertEquals("string should match", - "[farLeft [LatLng [latitude=52.0, longitude=-12.0, altitude=0.0]], " - + "farRight [LatLng [latitude=52.0, longitude=26.0, altitude=0.0]], " - + "nearLeft [LatLng [latitude=34.0, longitude=-12.0, altitude=0.0]], " - + "nearRight [LatLng [latitude=34.0, longitude=26.0, altitude=0.0]], " - + "latLngBounds [N:52.0; E:26.0; S:34.0; W:-12.0]]", - region.toString()); - } - - @Test - public void testParcelable() { - VisibleRegion region = new VisibleRegion(FAR_LEFT, FAR_RIGHT, NEAR_LEFT, NEAR_RIGHT, BOUNDS); - Parcelable parcel = MockParcel.obtain(region); - assertEquals("parcel should match initial object", region, parcel); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java deleted file mode 100644 index bebd82811..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/CompassEngineTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.hardware.Sensor; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.view.WindowManager; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static com.mapbox.mapboxsdk.location.LocationComponentCompassEngine.SENSOR_DELAY_MICROS; -import static junit.framework.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class CompassEngineTest { - - private LocationComponentCompassEngine compassEngine; - - @Mock - private WindowManager windowManager; - - @Mock - private SensorManager sensorManager; - - @Mock - private Sensor compassSensor; - - @Mock - private CompassListener compassListener; - - @Before - public void setUp() throws Exception { - when(sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)).thenReturn(compassSensor); - compassEngine = new LocationComponentCompassEngine(windowManager, sensorManager); - } - - @Test - public void lastKnownCompassBearingAccuracyDefault() { - assertEquals("Last accuracy should match", compassEngine.getLastAccuracySensorStatus(), 0); - } - - @Test - public void lastKnownCompassAccuracyStatusValue() { - Sensor sensor = mock(Sensor.class); - compassEngine.onAccuracyChanged(sensor, 2); - assertEquals("Last accuracy should match", compassEngine.getLastAccuracySensorStatus(), 2); - } - - @Test - public void whenGyroscopeIsNull_fallbackToGravity() { - SensorManager sensorManager = mock(SensorManager.class); - when(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)).thenReturn(null); - new LocationComponentCompassEngine(windowManager, sensorManager); - - verify(sensorManager, times(1)).getDefaultSensor(Sensor.TYPE_ACCELEROMETER); - } - - @Test - public void whenGyroscopeIsNull_fallbackToMagneticField() { - SensorManager sensorManager = mock(SensorManager.class); - when(sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE)).thenReturn(null); - new LocationComponentCompassEngine(windowManager, sensorManager); - - verify(sensorManager, times(1)).getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); - } - - @Test - public void listener_registerOnAdd() { - compassEngine.addCompassListener(compassListener); - verify(sensorManager) - .registerListener(any(SensorEventListener.class), eq(compassSensor), eq(SENSOR_DELAY_MICROS)); - } - - @Test - public void listener_unregisterOnRemove() { - compassEngine.addCompassListener(compassListener); - compassEngine.removeCompassListener(compassListener); - verify(sensorManager).unregisterListener(any(SensorEventListener.class), eq(compassSensor)); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt deleted file mode 100644 index 1b927d213..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationAnimatorCoordinatorTest.kt +++ /dev/null @@ -1,532 +0,0 @@ -package com.mapbox.mapboxsdk.location - -import android.animation.Animator -import android.location.Location -import android.util.SparseArray -import android.view.animation.LinearInterpolator -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.geometry.LatLng -import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_TILT_ANIM_DURATION -import com.mapbox.mapboxsdk.location.LocationComponentConstants.DEFAULT_TRACKING_ZOOM_ANIM_DURATION -import com.mapbox.mapboxsdk.location.MapboxAnimator.* -import com.mapbox.mapboxsdk.location.modes.CameraMode -import com.mapbox.mapboxsdk.location.modes.RenderMode -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Projection -import io.mockk.* -import junit.framework.Assert.assertEquals -import junit.framework.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class LocationAnimatorCoordinatorTest { - - private lateinit var locationAnimatorCoordinator: LocationAnimatorCoordinator - private val cameraPosition: CameraPosition = CameraPosition.DEFAULT - - private val animatorProvider: MapboxAnimatorProvider = mockk() - private val animatorSetProvider: MapboxAnimatorSetProvider = mockk() - - private val projection: Projection = mockk() - - @Before - fun setUp() { - locationAnimatorCoordinator = LocationAnimatorCoordinator(projection, animatorSetProvider, animatorProvider) - configureAnimatorProvider() - every { projection.getMetersPerPixelAtLatitude(any()) } answers { 1.0 } - every { animatorSetProvider.startAnimation(any(), any(), any()) } answers {} - locationAnimatorCoordinator.updateAnimatorListenerHolders(getListenerHoldersSet( - ANIMATOR_LAYER_LATLNG, - ANIMATOR_CAMERA_LATLNG, - ANIMATOR_LAYER_GPS_BEARING, - ANIMATOR_LAYER_COMPASS_BEARING, - ANIMATOR_CAMERA_GPS_BEARING, - ANIMATOR_CAMERA_COMPASS_BEARING, - ANIMATOR_LAYER_ACCURACY, - ANIMATOR_ZOOM, - ANIMATOR_TILT - )) - } - - private fun configureAnimatorProvider() { - // workaround https://github.com/mockk/mockk/issues/229#issuecomment-457816131 - registerInstanceFactory { AnimationsValueChangeListener {} } - registerInstanceFactory { AnimationsValueChangeListener {} } - val previousFloatSlot = slot() - val targetFloatSlot = slot() - val listenerSlot = slot>() - val maxFpsSlot = slot() - every { - animatorProvider.floatAnimator(capture(previousFloatSlot), capture(targetFloatSlot), capture(listenerSlot), capture(maxFpsSlot)) - } answers { - MapboxFloatAnimator(previousFloatSlot.captured, targetFloatSlot.captured, listenerSlot.captured, maxFpsSlot.captured) - } - - val previousLatLngSlot = slot() - val targetLatLngSlot = slot() - every { - animatorProvider.latLngAnimator(capture(previousLatLngSlot), capture(targetLatLngSlot), capture(listenerSlot), capture(maxFpsSlot)) - } answers { - MapboxLatLngAnimator(previousLatLngSlot.captured, targetLatLngSlot.captured, listenerSlot.captured, maxFpsSlot.captured) - } - - val callback = slot() - every { - animatorProvider.cameraAnimator(capture(previousFloatSlot), capture(targetFloatSlot), capture(listenerSlot), capture(callback)) - } answers { - MapboxCameraAnimatorAdapter(previousFloatSlot.captured, targetFloatSlot.captured, listenerSlot.captured, callback.captured) - } - every { - animatorProvider.cameraAnimator(capture(previousFloatSlot), capture(targetFloatSlot), capture(listenerSlot), null) - } answers { - MapboxCameraAnimatorAdapter(previousFloatSlot.captured, targetFloatSlot.captured, listenerSlot.captured, null) - } - } - - @Test - fun feedNewLocation_animatorsAreCreated() { - locationAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, false) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING] != null) - } - - @Test - fun feedNewLocation_animatorValue() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 35f - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val cameraLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG]?.target as LatLng - assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude) - - val layerLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG]?.target as LatLng - assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude) - - val cameraBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float - assertEquals(location.bearing, cameraBearingTarget) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(location.bearing, layerBearingTarget) - } - - @Test - fun feedNewLocation_animatorValue_correctRotation_1() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 0f - - val animator = mockk(relaxed = true) - every { animator.animatedValue } returns 270f - locationAnimatorCoordinator.animatorArray.put(ANIMATOR_LAYER_GPS_BEARING, animator) - - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(360f, layerBearingTarget) - } - - @Test - fun feedNewLocation_animatorValue_correctRotation_2() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 90f - - val animator = mockk(relaxed = true) - every { animator.animatedValue } returns 280f - locationAnimatorCoordinator.animatorArray.put(ANIMATOR_LAYER_GPS_BEARING, animator) - - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(450f, layerBearingTarget) - } - - @Test - fun feedNewLocation_animatorValue_correctRotation_3() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 300f - - val animator = mockk(relaxed = true) - every { animator.animatedValue } returns 450f - locationAnimatorCoordinator.animatorArray.put(ANIMATOR_LAYER_GPS_BEARING, animator) - - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(-60f, layerBearingTarget) - } - - @Test - fun feedNewLocation_animatorValue_correctRotation_4() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 350f - - val animator = mockk(relaxed = true) - every { animator.animatedValue } returns 10f - locationAnimatorCoordinator.animatorArray.put(ANIMATOR_LAYER_GPS_BEARING, animator) - - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(-10f, layerBearingTarget) - } - - @Test - fun feedNewLocation_animatorValue_correctRotation_5() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 90f - - val animator = mockk(relaxed = true) - every { animator.animatedValue } returns -280f - locationAnimatorCoordinator.animatorArray.put(ANIMATOR_LAYER_GPS_BEARING, animator) - - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(90f, layerBearingTarget) - } - - @Test - fun feedNewLocation_animatorValue_correctRotation_6() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 270f - - val animator = mockk(relaxed = true) - every { animator.animatedValue } returns -350f - locationAnimatorCoordinator.animatorArray.put(ANIMATOR_LAYER_GPS_BEARING, animator) - - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(-90f, layerBearingTarget) - } - - @Test - fun feedNewLocation_isNorth_animatorsAreCreated() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 35f - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, false) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING] != null) - } - - @Test - fun feedNewLocation_isNorth_animatorValue() { - val location = Location("") - location.latitude = 51.0 - location.longitude = 17.0 - location.bearing = 35f - locationAnimatorCoordinator.feedNewLocation(location, cameraPosition, true) - - val cameraLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG]?.target as LatLng - assertEquals(cameraLatLngTarget.latitude, cameraLatLngTarget.latitude) - - val layerLatLngTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_LATLNG]?.target as LatLng - assertEquals(layerLatLngTarget.latitude, layerLatLngTarget.latitude) - - val cameraBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_GPS_BEARING]?.target as Float - assertEquals(0f, cameraBearingTarget) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_GPS_BEARING]?.target as Float - assertEquals(location.bearing, layerBearingTarget) - } - - @Test - fun feedNewCompassBearing_animatorsAreCreated() { - locationAnimatorCoordinator.feedNewCompassBearing(77f, cameraPosition) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_COMPASS_BEARING] != null) - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_COMPASS_BEARING] != null) - } - - @Test - fun feedNewCompassBearing_animatorValue() { - val bearing = 77f - locationAnimatorCoordinator.feedNewCompassBearing(bearing, cameraPosition) - - val cameraBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_COMPASS_BEARING]?.target as Float - assertEquals(bearing, cameraBearingTarget) - - val layerBearingTarget = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_COMPASS_BEARING]?.target as Float - assertEquals(bearing, layerBearingTarget) - } - - @Test - fun feedNewAccuracyRadius_animatorsCreated() { - locationAnimatorCoordinator.feedNewAccuracyRadius(150f, false) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_ACCURACY] != null) - } - - @Test - fun feedNewAccuracyRadius_animatorValue() { - val accuracy = 150f - locationAnimatorCoordinator.feedNewAccuracyRadius(accuracy, false) - - val layerAccuracy = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_ACCURACY]?.target as Float - assertEquals(layerAccuracy, accuracy) - } - - @Test - fun feedNewAccuracyRadius_noAnimation_animatorsCreated() { - locationAnimatorCoordinator.feedNewAccuracyRadius(150f, true) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_ACCURACY] != null) - } - - @Test - fun feedNewAccuracyRadius_noAnimation_animatorValue() { - val accuracy = 150f - locationAnimatorCoordinator.feedNewAccuracyRadius(accuracy, true) - - val layerAccuracy = locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_ACCURACY]?.target as Float - assertEquals(layerAccuracy, accuracy) - } - - @Test - fun feedNewZoomLevel_animatorsCreated() { - locationAnimatorCoordinator.feedNewZoomLevel( - 15.0, - cameraPosition, - DEFAULT_TRACKING_ZOOM_ANIM_DURATION, - null - ) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_ZOOM] != null) - } - - @Test - fun feedNewZoomLevel_animatorValue() { - val zoom = 15.0f - locationAnimatorCoordinator.feedNewZoomLevel( - zoom.toDouble(), - cameraPosition, - DEFAULT_TRACKING_ZOOM_ANIM_DURATION, - null - ) - - val animator = locationAnimatorCoordinator.animatorArray[ANIMATOR_ZOOM] - assertEquals(zoom, animator.target) - verify { animatorSetProvider.startAnimation(eq(listOf(animator)), any(), DEFAULT_TRACKING_ZOOM_ANIM_DURATION) } - } - - @Test - fun feedNewTiltLevel_animatorsCreated() { - locationAnimatorCoordinator.feedNewTilt( - 30.0, - cameraPosition, - DEFAULT_TRACKING_TILT_ANIM_DURATION, - null - ) - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_TILT] != null) - } - - @Test - fun feedNewTiltLevel_animatorValue() { - val tilt = 30.0f - locationAnimatorCoordinator.feedNewTilt( - tilt.toDouble(), - cameraPosition, - DEFAULT_TRACKING_TILT_ANIM_DURATION, - null - ) - - val animator = locationAnimatorCoordinator.animatorArray[ANIMATOR_TILT] - assertEquals(tilt, animator.target) - verify { animatorSetProvider.startAnimation(eq(listOf(animator)), any(), DEFAULT_TRACKING_TILT_ANIM_DURATION) } - } - - @Test - fun cancelAllAnimators() { - locationAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, true) - locationAnimatorCoordinator.cancelAllAnimations() - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_LATLNG] == null) - } - - @Test - fun cancelZoomAnimators() { - locationAnimatorCoordinator.feedNewZoomLevel( - 15.0, - cameraPosition, - DEFAULT_TRACKING_ZOOM_ANIM_DURATION, - null - ) - locationAnimatorCoordinator.cancelZoomAnimation() - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_ZOOM] == null) - } - - @Test - fun cancelTiltAnimation() { - locationAnimatorCoordinator.feedNewTilt( - 30.0, - cameraPosition, - DEFAULT_TRACKING_TILT_ANIM_DURATION, - null - ) - - locationAnimatorCoordinator.cancelTiltAnimation() - - assertTrue(locationAnimatorCoordinator.animatorArray[ANIMATOR_TILT] == null) - } - - @Test - fun resetAllCameraAnimations_empty() { - locationAnimatorCoordinator.resetAllCameraAnimations(cameraPosition, false) - assertTrue(locationAnimatorCoordinator.animatorArray.size() == 0) - } - - @Test - fun resetAllLayerAnimations_empty() { - locationAnimatorCoordinator.resetAllLayerAnimations() - assertTrue(locationAnimatorCoordinator.animatorArray.size() == 0) - } - - @Test - fun addNewListener() { - val listener = Mockito.mock(AnimationsValueChangeListener::class.java) - val holder = AnimatorListenerHolder(RenderMode.NORMAL, listener) - val set = HashSet().also { - it.add(holder) - } - locationAnimatorCoordinator.updateAnimatorListenerHolders(set) - - assertTrue(locationAnimatorCoordinator.listeners.contains(listener)) - } - - @Test - fun updateListeners() { - val listener = Mockito.mock(AnimationsValueChangeListener::class.java) - val holder = AnimatorListenerHolder(RenderMode.NORMAL, listener) - val set = HashSet().also { - it.add(holder) - } - locationAnimatorCoordinator.updateAnimatorListenerHolders(set) - - val listener2 = Mockito.mock(AnimationsValueChangeListener::class.java) - val holder2 = AnimatorListenerHolder(RenderMode.NORMAL, listener2) - val listener3 = Mockito.mock(AnimationsValueChangeListener::class.java) - val holder3 = AnimatorListenerHolder(CameraMode.TRACKING_GPS, listener3) - val set2 = HashSet().also { - it.add(holder2) - it.add(holder3) - } - locationAnimatorCoordinator.updateAnimatorListenerHolders(set2) - - assertTrue(locationAnimatorCoordinator.listeners.size() == 2) - assertTrue(locationAnimatorCoordinator.listeners.contains(listener2)) - assertTrue(locationAnimatorCoordinator.listeners.contains(listener3)) - } - - @Test - fun feedNewCompassBearing_withAnimation() { - locationAnimatorCoordinator.setCompassAnimationEnabled(true) - locationAnimatorCoordinator.feedNewCompassBearing(77f, cameraPosition) - - val animators = mutableListOf( - locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_COMPASS_BEARING], - locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_COMPASS_BEARING]) - - verify(exactly = 1) { animatorSetProvider.startAnimation(eq(animators), ofType(LinearInterpolator::class), eq(LocationComponentConstants.COMPASS_UPDATE_RATE_MS)) } - } - - @Test - fun feedNewCompassBearing_withoutAnimation() { - locationAnimatorCoordinator.setCompassAnimationEnabled(false) - locationAnimatorCoordinator.feedNewCompassBearing(77f, cameraPosition) - - val animators = mutableListOf( - locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_COMPASS_BEARING], - locationAnimatorCoordinator.animatorArray[ANIMATOR_CAMERA_COMPASS_BEARING]) - - verify(exactly = 1) { animatorSetProvider.startAnimation(eq(animators), ofType(LinearInterpolator::class), eq(0)) } - } - - @Test - fun feedNewAccuracy_withAnimation() { - locationAnimatorCoordinator.setAccuracyAnimationEnabled(true) - locationAnimatorCoordinator.feedNewAccuracyRadius(150f, false) - - val animators = mutableListOf( - locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_ACCURACY]) - - verify(exactly = 1) { animatorSetProvider.startAnimation(eq(animators), ofType(LinearInterpolator::class), eq(LocationComponentConstants.ACCURACY_RADIUS_ANIMATION_DURATION)) } - } - - @Test - fun feedNewAccuracy_withoutAnimation() { - locationAnimatorCoordinator.setAccuracyAnimationEnabled(false) - locationAnimatorCoordinator.feedNewAccuracyRadius(150f, false) - - val animators = mutableListOf( - locationAnimatorCoordinator.animatorArray[ANIMATOR_LAYER_ACCURACY]) - - verify(exactly = 1) { animatorSetProvider.startAnimation(eq(animators), ofType(LinearInterpolator::class), eq(0)) } - } - - @Test - fun maxFps_setter() { - locationAnimatorCoordinator.setMaxAnimationFps(5) - assertEquals(5, locationAnimatorCoordinator.maxAnimationFps) - } - - @Test - fun maxFps_moreThanZeroRequired() { - locationAnimatorCoordinator.setMaxAnimationFps(0) - assertEquals(Int.MAX_VALUE, locationAnimatorCoordinator.maxAnimationFps) - locationAnimatorCoordinator.setMaxAnimationFps(-1) - assertEquals(Int.MAX_VALUE, locationAnimatorCoordinator.maxAnimationFps) - } - - @Test - fun maxFps_givenToAnimator() { - locationAnimatorCoordinator.setMaxAnimationFps(5) - locationAnimatorCoordinator.feedNewLocation(Location(""), cameraPosition, false) - verify { animatorProvider.latLngAnimator(any(), any(), any(), 5) } - verify { animatorProvider.floatAnimator(any(), any(), any(), 5) } - } - - private fun getListenerHoldersSet(vararg animatorTypes: Int): Set { - return HashSet().also { - for (type in animatorTypes) { - it.add(AnimatorListenerHolder(type, mockk())) - } - } - } -} - -private fun SparseArray.contains(listener: AnimationsValueChangeListener<*>?): Boolean { - for (i in 0 until this.size()) { - val element = this.get(this.keyAt(i)) - if (element == listener) { - return true - } - } - return false -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java deleted file mode 100644 index d1b8642c3..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationCameraControllerTest.java +++ /dev/null @@ -1,1001 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.graphics.PointF; -import android.location.Location; - -import com.mapbox.android.gestures.AndroidGesturesManager; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.camera.CameraUpdate; -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Projection; -import com.mapbox.mapboxsdk.maps.Transform; -import com.mapbox.mapboxsdk.maps.UiSettings; - -import junit.framework.Assert; - -import org.junit.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.Set; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_CAMERA_LATLNG; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_TILT; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_ZOOM; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.NONE; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.NONE_COMPASS; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.NONE_GPS; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.TRACKING; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.TRACKING_COMPASS; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.TRACKING_GPS; -import static com.mapbox.mapboxsdk.location.modes.CameraMode.TRACKING_GPS_NORTH; -import static junit.framework.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.nullable; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class LocationCameraControllerTest { - - @Test - public void setCameraMode_mapTransitionsAreCancelled() { - MapboxMap mapboxMap = mock(MapboxMap.class); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(TRACKING_GPS); - - verify(mapboxMap).cancelTransitions(); - } - - @Test - public void setCameraMode_gestureThresholdIsAdjusted() { - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - LocationCameraController camera = buildCamera(moveGestureDetector); - LocationComponentOptions options = mock(LocationComponentOptions.class); - float moveThreshold = 5f; - when(options.trackingInitialMoveThreshold()).thenReturn(moveThreshold); - when(options.trackingGesturesManagement()).thenReturn(true); - camera.initializeOptions(options); - - camera.setCameraMode(TRACKING_GPS); - - verify(moveGestureDetector).setMoveThreshold(moveThreshold); - } - - @Test - public void setCameraMode_gestureThresholdNotAdjustedWhenDisabled() { - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - LocationCameraController camera = buildCamera(moveGestureDetector); - LocationComponentOptions options = mock(LocationComponentOptions.class); - float moveThreshold = 5f; - when(options.trackingInitialMoveThreshold()).thenReturn(moveThreshold); - when(options.trackingGesturesManagement()).thenReturn(false); - camera.initializeOptions(options); - - camera.setCameraMode(TRACKING_GPS); - - verify(moveGestureDetector, times(0)).setMoveThreshold(moveThreshold); - verify(moveGestureDetector, times(0)).setMoveThreshold(0f); - } - - @Test - public void setCameraMode_gestureThresholdIsResetWhenNotTracking() { - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - LocationCameraController camera = buildCamera(moveGestureDetector); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.trackingGesturesManagement()).thenReturn(true); - camera.initializeOptions(options); - - camera.setCameraMode(NONE); - - verify(moveGestureDetector, times(2)).setMoveThreshold(0f); // one for initialization - } - - @Test - public void setCameraMode_notTrackingAdjustsFocalPoint() { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(TRACKING_GPS); - camera.setCameraMode(NONE); - - verify(mapboxMap.getUiSettings()).setFocalPoint(null); - } - - @Test - public void setCameraMode_trackingChangeListenerCameraDismissedIsCalled() { - OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - LocationCameraController camera = buildCamera(internalTrackingChangedListener); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(TRACKING_GPS); - camera.setCameraMode(NONE); - - verify(internalTrackingChangedListener).onCameraTrackingDismissed(); - } - - @Test - public void setCameraMode_internalCameraTrackingChangeListenerIsCalled() { - OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - LocationCameraController camera = buildCamera(internalTrackingChangedListener); - camera.initializeOptions(mock(LocationComponentOptions.class)); - int cameraMode = NONE; - - camera.setCameraMode(cameraMode); - - verify(internalTrackingChangedListener).onCameraTrackingChanged(cameraMode); - } - - @Test - public void setCameraMode_doNotNotifyAboutDuplicates_NONE() { - OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - LocationCameraController camera = buildCamera(internalTrackingChangedListener); - camera.initializeOptions(mock(LocationComponentOptions.class)); - int cameraMode = NONE; - - camera.setCameraMode(cameraMode); - camera.setCameraMode(cameraMode); - - verify(internalTrackingChangedListener, times(1)).onCameraTrackingChanged(cameraMode); - } - - @Test - public void setCameraMode_doNotNotifyAboutDuplicates_TRACKING_GPS() { - OnCameraTrackingChangedListener internalTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - LocationCameraController camera = buildCamera(internalTrackingChangedListener); - camera.initializeOptions(mock(LocationComponentOptions.class)); - int cameraMode = TRACKING_GPS; - - camera.setCameraMode(cameraMode); - camera.setCameraMode(cameraMode); - - verify(internalTrackingChangedListener, times(1)).onCameraTrackingChanged(cameraMode); - } - - @Test - public void setCameraMode_cancelTransitionsWhenSet() { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - when(mapboxMap.getProjection()).thenReturn(mock(Projection.class)); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(NONE_COMPASS); - verify(mapboxMap, times(1)).cancelTransitions(); - - camera.setCameraMode(NONE_GPS); - verify(mapboxMap, times(2)).cancelTransitions(); - - camera.setCameraMode(TRACKING); - verify(mapboxMap, times(3)).cancelTransitions(); - - camera.setCameraMode(TRACKING_COMPASS); - verify(mapboxMap, times(4)).cancelTransitions(); - - camera.setCameraMode(TRACKING_GPS); - verify(mapboxMap, times(5)).cancelTransitions(); - - camera.setCameraMode(TRACKING_GPS_NORTH); - verify(mapboxMap, times(6)).cancelTransitions(); - } - - @Test - public void setCameraMode_dontCancelTransitionsWhenNoneSet() { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - when(mapboxMap.getProjection()).thenReturn(mock(Projection.class)); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(NONE); - verify(mapboxMap, never()).cancelTransitions(); - } - - @Test - public void onNewLatLngValue_cameraModeTrackingUpdatesLatLng() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING); - LatLng latLng = mock(LatLng.class); - - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewLatLngValue_cameraModeTrackingGpsNorthUpdatesLatLng() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_GPS_NORTH); - LatLng latLng = mock(LatLng.class); - - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewLatLngValue_cameraModeTrackingGpsUpdatesLatLng() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_GPS); - LatLng latLng = mock(LatLng.class); - - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewLatLngValue_cameraModeTrackingCompassUpdatesLatLng() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_COMPASS); - LatLng latLng = mock(LatLng.class); - - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewLatLngValue_cameraModeNoneIgnored() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(NONE); - - assertNull(getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners())); - verify(transform, times(0)).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewLatLngValue_focalPointIsAdjusted() { - MapboxMap mapboxMap = mock(MapboxMap.class); - UiSettings uiSettings = mock(UiSettings.class); - when(mapboxMap.getUiSettings()).thenReturn(uiSettings); - Projection projection = mock(Projection.class); - PointF pointF = mock(PointF.class); - when(projection.toScreenLocation(any(LatLng.class))).thenReturn(pointF); - when(mapboxMap.getProjection()).thenReturn(projection); - LocationCameraController camera = buildCamera(mapboxMap); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.trackingGesturesManagement()).thenReturn(true); - camera.initializeOptions(options); - camera.setCameraMode(TRACKING); - LatLng latLng = mock(LatLng.class); - - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - - verify(uiSettings).setFocalPoint(pointF); - } - - @Test - public void onNewGpsBearingValue_cameraModeTrackingGpsUpdatesBearing() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_GPS); - float gpsBearing = 5f; - - getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners()).onNewAnimationValue(gpsBearing); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewGpsBearingValue_cameraModeNoneGpsUpdatesBearing() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(NONE_GPS); - float gpsBearing = 5f; - - getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners()).onNewAnimationValue(gpsBearing); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewGpsBearingValue_cameraModeTrackingNorthUpdatesBearing() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(mapboxMap, transform); - CameraPosition cameraPosition = new CameraPosition.Builder().bearing(7d).build(); - when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_GPS_NORTH); - float gpsBearing = 5f; - - getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners()).onNewAnimationValue(gpsBearing); - - verify(transform).moveCamera(eq(mapboxMap), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewGpsBearingValue_cameraModeTrackingNorthBearingZeroIgnored() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(mapboxMap, transform); - CameraPosition cameraPosition = new CameraPosition.Builder().bearing(0d).build(); - when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_GPS_NORTH); - float gpsBearing = 5f; - - getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners()).onNewAnimationValue(gpsBearing); - - verify(transform, times(0)).moveCamera(eq(mapboxMap), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewGpsBearingValue_cameraModeNoneIgnored() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(NONE); - - assertNull(getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners())); - verify(transform, times(0)).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewCompassBearingValue_cameraModeTrackingCompassUpdatesBearing() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING_COMPASS); - float compassBearing = 5f; - - getAnimationListener(ANIMATOR_CAMERA_COMPASS_BEARING, camera.getAnimationListeners()) - .onNewAnimationValue(compassBearing); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewCompassBearingValue_cameraModeNoneCompassUpdatesBearing() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(NONE_COMPASS); - float compassBearing = 5f; - - getAnimationListener(ANIMATOR_CAMERA_COMPASS_BEARING, camera.getAnimationListeners()) - .onNewAnimationValue(compassBearing); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewCompassBearingValue_cameraModeNoneIgnored() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(NONE); - - assertNull(getAnimationListener(ANIMATOR_CAMERA_COMPASS_BEARING, camera.getAnimationListeners())); - verify(transform, times(0)).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNewZoomValue_cameraIsUpdated() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING); - float zoom = 5f; - - getAnimationListener(ANIMATOR_ZOOM, camera.getAnimationListeners()).onNewAnimationValue(zoom); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void onNeTiltValue_cameraIsUpdated() { - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - camera.setCameraMode(TRACKING); - float tilt = 5f; - - getAnimationListener(ANIMATOR_TILT, camera.getAnimationListeners()).onNewAnimationValue(tilt); - - verify(transform).moveCamera(any(MapboxMap.class), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void gesturesManagement_enabled() { - MapboxMap mapboxMap = mock(MapboxMap.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - when(mapboxMap.getGesturesManager()).thenReturn(initialGesturesManager); - LocationCameraController camera = buildCamera(mapboxMap, initialGesturesManager, internalGesturesManager); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.trackingGesturesManagement()).thenReturn(true); - camera.initializeOptions(options); - - verify(mapboxMap).setGesturesManager(internalGesturesManager, true, true); - } - - @Test - public void gesturesManagement_disabled() { - MapboxMap mapboxMap = mock(MapboxMap.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - when(mapboxMap.getGesturesManager()).thenReturn(internalGesturesManager); - LocationCameraController camera = buildCamera(mapboxMap, initialGesturesManager, internalGesturesManager); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.trackingGesturesManagement()).thenReturn(false); - camera.initializeOptions(options); - - verify(mapboxMap).setGesturesManager(initialGesturesManager, true, true); - } - - @Test - public void gesturesManagement_optionNotChangedInitial() { - MapboxMap mapboxMap = mock(MapboxMap.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - when(mapboxMap.getGesturesManager()).thenReturn(initialGesturesManager); - LocationCameraController camera = buildCamera(mapboxMap, initialGesturesManager, internalGesturesManager); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.trackingGesturesManagement()).thenReturn(false); - camera.initializeOptions(options); - - verify(mapboxMap, times(0)).setGesturesManager(initialGesturesManager, true, true); - } - - @Test - public void gesturesManagement_optionNotChangedInternal() { - MapboxMap mapboxMap = mock(MapboxMap.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - when(mapboxMap.getGesturesManager()).thenReturn(internalGesturesManager); - LocationCameraController camera = buildCamera(mapboxMap, initialGesturesManager, internalGesturesManager); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.trackingGesturesManagement()).thenReturn(true); - camera.initializeOptions(options); - - verify(mapboxMap, times(0)).setGesturesManager(internalGesturesManager, true, true); - } - - @Test - public void onMove_notCancellingTransitionWhileNone() { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(NONE); - camera.onMoveListener.onMove(moveGestureDetector); - verify(mapboxMap, times(0)).cancelTransitions(); - verify(moveGestureDetector, times(0)).interrupt(); - - // testing subsequent calls - camera.onMoveListener.onMove(moveGestureDetector); - verify(mapboxMap, times(0)).cancelTransitions(); - verify(moveGestureDetector, times(0)).interrupt(); - } - - @Test - public void onMove_cancellingTransitionWhileGps() { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(TRACKING); - camera.onMoveListener.onMove(moveGestureDetector); - verify(mapboxMap, times(1)).cancelTransitions(); - verify(moveGestureDetector, times(1)).interrupt(); - - // testing subsequent calls - camera.onMoveListener.onMove(moveGestureDetector); - verify(mapboxMap, times(1)).cancelTransitions(); - verify(moveGestureDetector, times(1)).interrupt(); - } - - @Test - public void onMove_cancellingTransitionWhileBearing() { - MapboxMap mapboxMap = mock(MapboxMap.class); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - LocationCameraController camera = buildCamera(mapboxMap); - camera.initializeOptions(mock(LocationComponentOptions.class)); - - camera.setCameraMode(NONE_COMPASS); - camera.onMoveListener.onMove(moveGestureDetector); - verify(mapboxMap, times(1)).cancelTransitions(); - verify(moveGestureDetector, times(1)).interrupt(); - - // testing subsequent calls - camera.onMoveListener.onMove(moveGestureDetector); - verify(mapboxMap, times(1)).cancelTransitions(); - verify(moveGestureDetector, times(1)).interrupt(); - } - - @Test - public void transition_locationIsNull() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - - camera.setCameraMode(TRACKING, null, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - Assert.assertEquals(TRACKING, camera.getCameraMode()); - verify(listener).onLocationCameraTransitionFinished(TRACKING); - verify(transform, times(0)) - .animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_notTracking() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - - camera.setCameraMode(NONE, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - verify(listener, times(1)).onLocationCameraTransitionFinished(NONE); - verify(transform, times(0)) - .animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_trackingChanged() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - listener.onLocationCameraTransitionFinished(TRACKING); - return null; - } - }).when(transform).animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - - camera.setCameraMode(TRACKING, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - verify(listener).onLocationCameraTransitionFinished(TRACKING); - verify(transform) - .animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_trackingNotChanged() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - - camera.setCameraMode(TRACKING, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - listener.onLocationCameraTransitionFinished(TRACKING_GPS_NORTH); - return null; - } - }).when(transform).animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - - camera.setCameraMode(TRACKING_GPS_NORTH, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - verify(listener, times(1)).onLocationCameraTransitionFinished(TRACKING_GPS_NORTH); - verify(transform, times(1)) - .animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_canceled() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - - doAnswer(new Answer() { - @Override - public Void answer(InvocationOnMock invocation) throws Throwable { - listener.onLocationCameraTransitionCanceled(TRACKING); - return null; - } - }).when(transform).animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - - camera.setCameraMode(TRACKING, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - verify(listener).onLocationCameraTransitionCanceled(TRACKING); - verify(transform) - .animateCamera(eq(mapboxMap), any(CameraUpdate.class), any(Integer.class), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_mapboxCallbackFinished() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - when(location.getLatitude()).thenReturn(1.0); - when(location.getLongitude()).thenReturn(1.0); - when(location.getBearing()).thenReturn(30f); - when(location.getAltitude()).thenReturn(0.0); - - ArgumentCaptor callbackCaptor - = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class); - - camera.setCameraMode(TRACKING, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)); - verify(transform).animateCamera( - eq(mapboxMap), - eq(CameraUpdateFactory.newCameraPosition(builder.build())), - eq((int) TRANSITION_ANIMATION_DURATION_MS), - callbackCaptor.capture()); - - Assert.assertTrue(camera.isTransitioning()); - - callbackCaptor.getValue().onFinish(); - - Assert.assertFalse(camera.isTransitioning()); - - verify(listener).onLocationCameraTransitionFinished(TRACKING); - } - - @Test - public void transition_mapboxCallbackFinishedImmediately() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - when(location.getLatitude()).thenReturn(1.0); - when(location.getLongitude()).thenReturn(1.0); - when(location.getBearing()).thenReturn(30f); - when(location.getAltitude()).thenReturn(0.0); - - ArgumentCaptor callbackCaptor - = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class); - - camera.setCameraMode(TRACKING, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)); - verify(transform).moveCamera( - eq(mapboxMap), - eq(CameraUpdateFactory.newCameraPosition(builder.build())), - callbackCaptor.capture()); - - Assert.assertTrue(camera.isTransitioning()); - - callbackCaptor.getValue().onFinish(); - - Assert.assertFalse(camera.isTransitioning()); - - verify(listener).onLocationCameraTransitionFinished(TRACKING); - } - - @Test - public void transition_mapboxCallbackCanceled() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - when(location.getLatitude()).thenReturn(1.0); - when(location.getLongitude()).thenReturn(1.0); - when(location.getBearing()).thenReturn(30f); - when(location.getAltitude()).thenReturn(0.0); - - ArgumentCaptor callbackCaptor - = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class); - - camera.setCameraMode(TRACKING, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)); - verify(transform).animateCamera( - eq(mapboxMap), - eq(CameraUpdateFactory.newCameraPosition(builder.build())), - eq((int) TRANSITION_ANIMATION_DURATION_MS), - callbackCaptor.capture()); - - Assert.assertTrue(camera.isTransitioning()); - - callbackCaptor.getValue().onCancel(); - - Assert.assertFalse(camera.isTransitioning()); - - verify(listener).onLocationCameraTransitionCanceled(TRACKING); - } - - @Test - public void transition_mapboxAnimateBearing() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - when(location.getLatitude()).thenReturn(1.0); - when(location.getLongitude()).thenReturn(1.0); - when(location.getBearing()).thenReturn(30f); - when(location.getAltitude()).thenReturn(0.0); - - camera.setCameraMode(TRACKING_GPS, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)).bearing(30); - verify(transform).animateCamera( - eq(mapboxMap), - eq(CameraUpdateFactory.newCameraPosition(builder.build())), - eq((int) TRANSITION_ANIMATION_DURATION_MS), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_mapboxAnimateNorth() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - when(location.getLatitude()).thenReturn(1.0); - when(location.getLongitude()).thenReturn(1.0); - when(location.getBearing()).thenReturn(30f); - when(location.getAltitude()).thenReturn(0.0); - - camera.setCameraMode(TRACKING_GPS_NORTH, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - CameraPosition.Builder builder = new CameraPosition.Builder().target(new LatLng(location)).bearing(0); - verify(transform).animateCamera( - eq(mapboxMap), - eq(CameraUpdateFactory.newCameraPosition(builder.build())), - eq((int) TRANSITION_ANIMATION_DURATION_MS), - any(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_animatorValuesDuringTransition() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - final OnLocationCameraTransitionListener listener = mock(OnLocationCameraTransitionListener.class); - Location location = mock(Location.class); - - ArgumentCaptor callbackCaptor - = ArgumentCaptor.forClass(MapboxMap.CancelableCallback.class); - - camera.setCameraMode(TRACKING_GPS, location, TRANSITION_ANIMATION_DURATION_MS, null, null, null, listener); - - verify(transform).animateCamera( - eq(mapboxMap), - any(CameraUpdate.class), - eq((int) TRANSITION_ANIMATION_DURATION_MS), - callbackCaptor.capture()); - - LatLng latLng = new LatLng(10, 10); - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners()).onNewAnimationValue(10f); - getAnimationListener(ANIMATOR_TILT, camera.getAnimationListeners()).onNewAnimationValue(10f); - getAnimationListener(ANIMATOR_ZOOM, camera.getAnimationListeners()).onNewAnimationValue(10f); - - verify(transform, times(0)).moveCamera(eq(mapboxMap), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - - callbackCaptor.getValue().onFinish(); - - getAnimationListener(ANIMATOR_CAMERA_LATLNG, camera.getAnimationListeners()).onNewAnimationValue(latLng); - getAnimationListener(ANIMATOR_CAMERA_GPS_BEARING, camera.getAnimationListeners()).onNewAnimationValue(10f); - getAnimationListener(ANIMATOR_TILT, camera.getAnimationListeners()).onNewAnimationValue(10f); - getAnimationListener(ANIMATOR_ZOOM, camera.getAnimationListeners()).onNewAnimationValue(10f); - - verify(transform, times(4)).moveCamera(eq(mapboxMap), any(CameraUpdate.class), - nullable(MapboxMap.CancelableCallback.class)); - } - - @Test - public void transition_customAnimation() { - MapboxMap mapboxMap = mock(MapboxMap.class); - Transform transform = mock(Transform.class); - when(mapboxMap.getCameraPosition()).thenReturn(CameraPosition.DEFAULT); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - LocationCameraController camera = buildCamera(mapboxMap, transform); - camera.initializeOptions(mock(LocationComponentOptions.class)); - Location location = mock(Location.class); - CameraUpdate cameraUpdate = CameraUpdateFactory.newCameraPosition( - new CameraPosition.Builder() - .target(new LatLng(location)) - .zoom(14.0) - .bearing(13.0) - .tilt(45.0) - .build() - ); - - camera.setCameraMode(TRACKING, location, 1200, 14.0, 13.0, 45.0, null); - verify(transform) - .animateCamera(eq(mapboxMap), eq(cameraUpdate), eq(1200), any(MapboxMap.CancelableCallback.class)); - } - - private LocationCameraController buildCamera(OnCameraTrackingChangedListener onCameraTrackingChangedListener) { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - Transform transform = mock(Transform.class); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - return new LocationCameraController(mapboxMap, transform, moveGestureDetector, - onCameraTrackingChangedListener, onCameraMoveInvalidateListener, initialGesturesManager, internalGesturesManager); - } - - private LocationCameraController buildCamera(MoveGestureDetector moveGestureDetector) { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - Transform transform = mock(Transform.class); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - return new LocationCameraController(mapboxMap, transform, moveGestureDetector, - onCameraTrackingChangedListener, onCameraMoveInvalidateListener, initialGesturesManager, internalGesturesManager); - } - - private LocationCameraController buildCamera(MapboxMap mapboxMap) { - Transform transform = mock(Transform.class); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - return new LocationCameraController(mapboxMap, transform, moveGestureDetector, - onCameraTrackingChangedListener, onCameraMoveInvalidateListener, initialGesturesManager, internalGesturesManager); - } - - private LocationCameraController buildCamera(Transform transform) { - MapboxMap mapboxMap = mock(MapboxMap.class); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - Projection projection = mock(Projection.class); - when(mapboxMap.getProjection()).thenReturn(projection); - when(projection.getMetersPerPixelAtLatitude(any(Double.class))).thenReturn(Double.valueOf(1000)); - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - return new LocationCameraController(mapboxMap, transform, moveGestureDetector, - onCameraTrackingChangedListener, onCameraMoveInvalidateListener, initialGesturesManager, internalGesturesManager); - } - - private LocationCameraController buildCamera(MapboxMap mapboxMap, Transform transform) { - when(mapboxMap.getUiSettings()).thenReturn(mock(UiSettings.class)); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class); - AndroidGesturesManager initialGesturesManager = mock(AndroidGesturesManager.class); - AndroidGesturesManager internalGesturesManager = mock(AndroidGesturesManager.class); - return new LocationCameraController(mapboxMap, transform, moveGestureDetector, - onCameraTrackingChangedListener, onCameraMoveInvalidateListener, initialGesturesManager, internalGesturesManager); - } - - private LocationCameraController buildCamera(MapboxMap mapboxMap, AndroidGesturesManager initialGesturesManager, - AndroidGesturesManager internalGesturesManager) { - Transform transform = mock(Transform.class); - MoveGestureDetector moveGestureDetector = mock(MoveGestureDetector.class); - OnCameraTrackingChangedListener onCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener.class); - OnCameraMoveInvalidateListener onCameraMoveInvalidateListener = mock(OnCameraMoveInvalidateListener.class); - return new LocationCameraController(mapboxMap, transform, moveGestureDetector, - onCameraTrackingChangedListener, onCameraMoveInvalidateListener, initialGesturesManager, internalGesturesManager); - } - - private MapboxAnimator.AnimationsValueChangeListener getAnimationListener( - @MapboxAnimator.Type int animatorType, - Set holders) { - for (AnimatorListenerHolder holder : holders) { - @MapboxAnimator.Type int type = holder.getAnimatorType(); - if (type == animatorType) { - return holder.getListener(); - } - } - - return null; - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptionsTest.java deleted file mode 100644 index 2660c819d..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentActivationOptionsTest.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.support.annotation.NonNull; - -import com.mapbox.mapboxsdk.R; -import com.mapbox.mapboxsdk.maps.Style; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.junit.MockitoRule; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class LocationComponentActivationOptionsTest { - - @Mock - private Context context; - @Mock - private TypedArray array; - @Mock - private Resources resources; - @Mock - private Style style; - - @Rule - public MockitoRule mockitoRule = MockitoJUnit.rule(); - - @NonNull - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Before - public void setUp() throws Exception { - when(context.obtainStyledAttributes(R.style.mapbox_LocationComponent, R.styleable.mapbox_LocationComponent)) - .thenReturn(array); - when(array.getResourceId(R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1)) - .thenReturn(R.drawable.mapbox_user_icon); - when(context.getResources()).thenReturn(resources); - } - - @Test - public void sanity() throws Exception { - when(style.isFullyLoaded()).thenReturn(true); - - LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(context) - .accuracyAlpha(0.5f) - .build(); - assertNotNull(locationComponentOptions); - - LocationComponentActivationOptions locationComponentActivationOptions = - LocationComponentActivationOptions.builder(context, style) - .locationComponentOptions(locationComponentOptions) - .useDefaultLocationEngine(true) - .build(); - assertNotNull(locationComponentActivationOptions); - } - - @Test - public void includingBothStyleResAndComponentOptions_causesExceptionToBeThrown() throws Exception { - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("You've provided both a style resource and a LocationComponentOptions" - + " object to the LocationComponentActivationOptions builder. You can't use both and " - + "you must choose one of the two to style the LocationComponent."); - - LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(context) - .accuracyAlpha(0.5f) - .build(); - - LocationComponentActivationOptions.builder(context, style) - .locationComponentOptions(locationComponentOptions) - .styleRes(R.style.mapbox_LocationComponent) - .build(); - } - - @Test - public void nullContext_causesExceptionToBeThrown() throws Exception { - thrown.expect(NullPointerException.class); - thrown.expectMessage("Context in LocationComponentActivationOptions is null."); - - LocationComponentActivationOptions.builder(null, style) - .build(); - } - - @Test - public void nullStyle_causesExceptionToBeThrown() throws Exception { - thrown.expect(NullPointerException.class); - thrown.expectMessage("Style in LocationComponentActivationOptions is null. Make sure the Style object isn't null." - + " Wait for the map to fully load before passing the Style object to LocationComponentActivationOptions."); - - LocationComponentActivationOptions.builder(context, null) - .build(); - } - - @Test - public void locationComponent_exceptionThrownWithDefaultLocationEngineButNotFullyLoadedStyle() throws Exception { - - when(style.isFullyLoaded()).thenReturn(false); - - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Style in LocationComponentActivationOptions isn't fully loaded. Wait for the " - + "map to fully load before passing the Style object to " - + "LocationComponentActivationOptions."); - - LocationComponentActivationOptions.builder(context, style) - .build(); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java deleted file mode 100644 index b1e1f14cb..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentOptionsTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; - -import android.support.annotation.NonNull; -import com.mapbox.mapboxsdk.R; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnitRunner; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.when; - -@RunWith(MockitoJUnitRunner.class) -public class LocationComponentOptionsTest { - - @Mock - private Context context; - @Mock - private TypedArray array; - @Mock - private Resources resources; - - @NonNull - @Rule - public ExpectedException thrown = ExpectedException.none(); - - @Before - public void setUp() throws Exception { - when(context.obtainStyledAttributes(R.style.mapbox_LocationComponent, R.styleable.mapbox_LocationComponent)) - .thenReturn(array); - when(array.getResourceId(R.styleable.mapbox_LocationComponent_mapbox_foregroundDrawable, -1)) - .thenReturn(R.drawable.mapbox_user_icon); - when(context.getResources()).thenReturn(resources); - } - - @Test - public void sanity() throws Exception { - LocationComponentOptions locationComponentOptions = LocationComponentOptions.builder(context) - .accuracyAlpha(0.5f) - .build(); - assertNotNull(locationComponentOptions); - } - - @Test - public void passingOutOfRangeAccuracyAlpha_throwsException() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Accuracy alpha value must be between 0.0 and " - + "1.0."); - LocationComponentOptions.builder(context) - .accuracyAlpha(2f) - .build(); - } - - @Test - public void negativeElevation_causesExceptionToBeThrown() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("Invalid shadow size -500.0. Must be >= 0"); - LocationComponentOptions.builder(context) - .elevation(-500) - .build(); - } - - @Test - public void passingBothLayerPositionOptions_throwsException() throws Exception { - thrown.expect(IllegalArgumentException.class); - thrown.expectMessage("You cannot set both layerAbove and layerBelow options." - + "Choose one or the other."); - LocationComponentOptions.builder(context) - .layerAbove("above") - .layerBelow("below") - .build(); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManagerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManagerTest.kt deleted file mode 100644 index c8a75b670..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentPositionManagerTest.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.mapbox.mapboxsdk.location - -import com.mapbox.mapboxsdk.maps.Style -import com.mapbox.mapboxsdk.style.layers.Layer -import io.mockk.mockk -import io.mockk.verify -import junit.framework.Assert.assertFalse -import junit.framework.Assert.assertTrue -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner - -@RunWith(MockitoJUnitRunner::class) -class LocationComponentPositionManagerTest { - - private lateinit var style: Style - private lateinit var layer: Layer - - @Before - fun setup() { - style = mockk(relaxed = true) - layer = mockk(relaxed = true) - } - - @Test - fun update_noChange_null() { - val positionManager = LocationComponentPositionManager(style, null, null) - val requiresUpdate = positionManager.update(null, null) - assertFalse(requiresUpdate) - } - - @Test - fun update_noChange_above() { - val positionManager = LocationComponentPositionManager(style, "above", null) - val requiresUpdate = positionManager.update("above", null) - assertFalse(requiresUpdate) - } - - @Test - fun update_noChange_below() { - val positionManager = LocationComponentPositionManager(style, null, "below") - val requiresUpdate = positionManager.update(null, "below") - assertFalse(requiresUpdate) - } - - @Test - fun update_fromNull_above() { - val positionManager = LocationComponentPositionManager(style, null, null) - val requiresUpdate = positionManager.update("above", null) - assertTrue(requiresUpdate) - } - - @Test - fun update_fromNull_below() { - val positionManager = LocationComponentPositionManager(style, null, null) - val requiresUpdate = positionManager.update(null, "below") - assertTrue(requiresUpdate) - } - - @Test - fun update_toNull_above() { - val positionManager = LocationComponentPositionManager(style, "above", null) - val requiresUpdate = positionManager.update(null, null) - assertTrue(requiresUpdate) - } - - @Test - fun update_toNull_below() { - val positionManager = LocationComponentPositionManager(style, null, "below") - val requiresUpdate = positionManager.update(null, null) - assertTrue(requiresUpdate) - } - - @Test - fun update_fromValue_above() { - val positionManager = LocationComponentPositionManager(style, "above1", null) - val requiresUpdate = positionManager.update("above2", null) - assertTrue(requiresUpdate) - } - - @Test - fun update_fromValue_below() { - val positionManager = LocationComponentPositionManager(style, null, "below1") - val requiresUpdate = positionManager.update(null, "below2") - assertTrue(requiresUpdate) - } - - @Test - fun addLayer_noModifier() { - val positionManager = LocationComponentPositionManager(style, null, null) - positionManager.addLayerToMap(layer) - - verify { style.addLayer(layer) } - } - - @Test - fun addLayer_above() { - val positionManager = LocationComponentPositionManager(style, "above", null) - positionManager.addLayerToMap(layer) - - verify { style.addLayerAbove(layer, "above") } - } - - @Test - fun addLayer_below() { - val positionManager = LocationComponentPositionManager(style, null, "below") - positionManager.addLayerToMap(layer) - - verify { style.addLayerBelow(layer, "below") } - } - - @Test - fun addLayer_afterUpdate_above() { - val positionManager = LocationComponentPositionManager(style, null, null) - positionManager.update("above", null) - positionManager.addLayerToMap(layer) - - verify { style.addLayerAbove(layer, "above") } - } - - @Test - fun addLayer_afterUpdate_below() { - val positionManager = LocationComponentPositionManager(style, null, null) - positionManager.update(null, "below") - positionManager.addLayerToMap(layer) - - verify { style.addLayerBelow(layer, "below") } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt deleted file mode 100644 index 2994c29d5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationComponentTest.kt +++ /dev/null @@ -1,453 +0,0 @@ -package com.mapbox.mapboxsdk.location - -import android.content.Context -import android.content.res.Resources -import android.content.res.TypedArray -import android.location.Location -import android.os.Looper -import com.mapbox.android.core.location.LocationEngine -import com.mapbox.android.core.location.LocationEngineRequest -import com.mapbox.mapboxsdk.R -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.location.LocationComponentConstants.TRANSITION_ANIMATION_DURATION_MS -import com.mapbox.mapboxsdk.location.modes.CameraMode -import com.mapbox.mapboxsdk.location.modes.RenderMode -import com.mapbox.mapboxsdk.maps.MapboxMap -import com.mapbox.mapboxsdk.maps.Style -import com.mapbox.mapboxsdk.maps.Transform -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Mock -import org.mockito.Mockito.* -import org.mockito.MockitoAnnotations -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class LocationComponentTest { - private lateinit var locationComponent: LocationComponent - - @Mock - private lateinit var locationComponentOptions: LocationComponentOptions - - @Mock - private lateinit var mapboxMap: MapboxMap - - @Mock - private lateinit var transform: Transform - - @Mock - private lateinit var context: Context - - @Mock - private lateinit var locationEngine: LocationEngine - - @Mock - private lateinit var locationEngineRequest: LocationEngineRequest - - @Mock - private lateinit var currentListener: LocationComponent.CurrentLocationEngineCallback - - @Mock - private lateinit var lastListener: LocationComponent.LastLocationEngineCallback - - @Mock - private lateinit var compassEngine: CompassEngine - - @Mock - private lateinit var locationLayerController: LocationLayerController - - @Mock - private lateinit var locationCameraController: LocationCameraController - - @Mock - private lateinit var locationAnimatorCoordinator: LocationAnimatorCoordinator - - @Mock - private lateinit var staleStateManager: StaleStateManager - - @Mock - private lateinit var locationEngineProvider: LocationComponent.InternalLocationEngineProvider - - @Mock - private lateinit var style: Style - - private lateinit var developerAnimationListeners: List - - @Before - fun before() { - MockitoAnnotations.initMocks(this) - developerAnimationListeners = mutableListOf() - locationComponent = LocationComponent(mapboxMap, transform, developerAnimationListeners, currentListener, lastListener, locationLayerController, locationCameraController, locationAnimatorCoordinator, staleStateManager, compassEngine, locationEngineProvider) - doReturn(locationEngine).`when`(locationEngineProvider).getBestLocationEngine(context, false) - doReturn(style).`when`(mapboxMap).style - } - - @Test - fun activateWithRequestTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - - Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest) - - doReturn(mock(TypedArray::class.java)).`when`(context) - .obtainStyledAttributes(R.style.mapbox_LocationComponent, R.styleable.mapbox_LocationComponent) - - val resources = mock(Resources::class.java) - - doReturn(resources).`when`(context).resources - doReturn(0f).`when`(resources) - .getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold) - doReturn(0f).`when`(resources) - .getDimension(R.dimen.mapbox_locationComponentTrackingMultiFingerMoveThreshold) - locationComponent.activateLocationComponent(context, mock(Style::class.java), true, locationEngineRequest) - Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest) - } - - @Test - fun activateWithDefaultLocationEngineRequestAndOptionsTestDefaultLocationEngine() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), true, locationEngineRequest, locationComponentOptions) - Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest) - Assert.assertNotNull(locationComponent.locationEngine) - } - - @Test - fun activateWithDefaultLocationEngineRequestAndOptionsTestCustomLocationEngine() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), false, locationEngineRequest, locationComponentOptions) - Assert.assertEquals(locationEngineRequest, locationComponent.locationEngineRequest) - Assert.assertNull(locationComponent.locationEngine) - } - - @Test - fun locationUpdatesWhenEnabledDisableTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - verify(locationEngine, times(0)).removeLocationUpdates(currentListener) - verify(locationEngine, times(0)).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) - - locationComponent.onStart() - verify(locationEngine, times(0)).removeLocationUpdates(currentListener) - verify(locationEngine, times(0)).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) - - locationComponent.isLocationComponentEnabled = true - verify(locationEngine).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) - - locationComponent.isLocationComponentEnabled = false - verify(locationEngine).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) - verify(locationEngine).removeLocationUpdates(currentListener) - } - - @Test - fun locationUpdatesWhenStartedStoppedTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - - locationComponent.onStop() - verify(locationEngine).removeLocationUpdates(currentListener) - - locationComponent.onStart() - verify(locationEngine, times(2)).requestLocationUpdates(eq(locationEngineRequest), eq(currentListener), any(Looper::class.java)) - } - - @Test - fun locationUpdatesWhenNewRequestTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - - val newRequest = mock(LocationEngineRequest::class.java) - locationComponent.locationEngineRequest = newRequest - verify(locationEngine).removeLocationUpdates(currentListener) - verify(locationEngine).requestLocationUpdates(eq(newRequest), eq(currentListener), any(Looper::class.java)) - } - - @Test - fun lastLocationUpdateOnStartTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - - verify(locationEngine).getLastLocation(lastListener) - } - - @Test - fun transitionCallbackFinishedTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - val listener = mock(OnLocationCameraTransitionListener::class.java) - - val callback = ArgumentCaptor.forClass(OnLocationCameraTransitionListener::class.java) - locationComponent.setCameraMode(CameraMode.TRACKING, listener) - verify(locationCameraController).setCameraMode(eq(CameraMode.TRACKING), any(), eq(TRANSITION_ANIMATION_DURATION_MS), isNull(), isNull(), isNull(), callback.capture()) - callback.value.onLocationCameraTransitionFinished(CameraMode.TRACKING) - - verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING) - verify(locationAnimatorCoordinator).resetAllCameraAnimations(CameraPosition.DEFAULT, false) - } - - @Test - fun transitionCallbackCanceledTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - val listener = mock(OnLocationCameraTransitionListener::class.java) - - val callback = ArgumentCaptor.forClass(OnLocationCameraTransitionListener::class.java) - locationComponent.setCameraMode(CameraMode.TRACKING, listener) - verify(locationCameraController).setCameraMode(eq(CameraMode.TRACKING), any(), eq(TRANSITION_ANIMATION_DURATION_MS), isNull(), isNull(), isNull(), callback.capture()) - callback.value.onLocationCameraTransitionCanceled(CameraMode.TRACKING) - - verify(listener).onLocationCameraTransitionCanceled(CameraMode.TRACKING) - verify(locationAnimatorCoordinator).resetAllCameraAnimations(CameraPosition.DEFAULT, false) - } - - @Test - fun transitionCustomFinishedTest() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - val listener = mock(OnLocationCameraTransitionListener::class.java) - - val callback = ArgumentCaptor.forClass(OnLocationCameraTransitionListener::class.java) - locationComponent.setCameraMode(CameraMode.TRACKING, 1200, 14.0, 13.0, 45.0, listener) - verify(locationCameraController).setCameraMode(eq(CameraMode.TRACKING), any(), eq(1200L), eq(14.0), eq(13.0), eq(45.0), callback.capture()) - callback.value.onLocationCameraTransitionFinished(CameraMode.TRACKING) - - verify(listener).onLocationCameraTransitionFinished(CameraMode.TRACKING) - verify(locationAnimatorCoordinator).resetAllCameraAnimations(CameraPosition.DEFAULT, false) - } - - @Test - fun compass_listenWhenConsumedByNoneCamera() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationCameraController.isConsumingCompass).thenReturn(true) - locationComponent.cameraMode = CameraMode.NONE_COMPASS - verify(compassEngine).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_listenWhenConsumedByTrackingCamera() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationCameraController.isConsumingCompass).thenReturn(true) - locationComponent.cameraMode = CameraMode.TRACKING_COMPASS - verify(compassEngine).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_listenWhenConsumedByLayer() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - verify(compassEngine).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_notListenWhenNotConsumed() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(false) - `when`(locationCameraController.isConsumingCompass).thenReturn(false) - locationComponent.renderMode = RenderMode.GPS - locationComponent.renderMode = RenderMode.NORMAL - locationComponent.cameraMode = CameraMode.TRACKING - locationComponent.cameraMode = CameraMode.NONE - locationComponent.cameraMode = CameraMode.NONE_GPS - locationComponent.cameraMode = CameraMode.TRACKING_GPS - locationComponent.cameraMode = CameraMode.TRACKING_GPS_NORTH - verify(compassEngine, never()).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_removeListenerOnChange() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - `when`(locationLayerController.isConsumingCompass).thenReturn(false) - locationComponent.renderMode = RenderMode.NORMAL - verify(compassEngine).removeCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_removeListenerOnStop() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - locationComponent.onStop() - verify(compassEngine).removeCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_reAddListenerOnStart() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - locationComponent.onStop() - locationComponent.onStart() - verify(compassEngine, times(2)).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_removeListenerOnStyleStartLoad() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - locationComponent.onStartLoadingMap() - verify(compassEngine).removeCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_reAddListenerOnStyleLoadFinished() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - locationComponent.onStartLoadingMap() - locationComponent.onFinishLoadingStyle() - verify(compassEngine, times(2)).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_reAddListenerOnlyWhenEnabled() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - locationComponent.isLocationComponentEnabled = false - - locationComponent.onStartLoadingMap() - locationComponent.onFinishLoadingStyle() - verify(compassEngine).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_notAdListenerWhenDisabled() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.onStart() - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - verify(compassEngine, never()).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun compass_notAdListenerWhenStopped() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.isLocationComponentEnabled = true - `when`(mapboxMap.cameraPosition).thenReturn(CameraPosition.DEFAULT) - - `when`(locationLayerController.isConsumingCompass).thenReturn(true) - locationComponent.renderMode = RenderMode.COMPASS - verify(compassEngine, never()).addCompassListener(any(CompassListener::class.java)) - } - - @Test - fun developerAnimationCalled() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.isLocationComponentEnabled = true - for (listener in developerAnimationListeners) { - listener.onDeveloperAnimationStarted() - } - verify(locationCameraController).setCameraMode(eq(CameraMode.NONE), isNull(), eq(TRANSITION_ANIMATION_DURATION_MS), isNull(), isNull(), isNull(), any()) - } - - @Test - fun internal_cameraTrackingChangedListener_onCameraTrackingDismissed() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.isLocationComponentEnabled = true - - val cameraChangeListener: OnCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener::class.java) - locationComponent.addOnCameraTrackingChangedListener(cameraChangeListener) - - locationComponent.cameraTrackingChangedListener.onCameraTrackingDismissed() - - verify(cameraChangeListener).onCameraTrackingDismissed() - } - - @Test - fun internal_cameraTrackingChangedListener_onCameraTrackingChanged() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.isLocationComponentEnabled = true - - val cameraValueListener: AnimatorListenerHolder = mock(AnimatorListenerHolder::class.java) - val layerValueListener: AnimatorListenerHolder = mock(AnimatorListenerHolder::class.java) - `when`(locationCameraController.animationListeners).thenReturn(setOf(cameraValueListener)) - `when`(locationLayerController.animationListeners).thenReturn(setOf(layerValueListener)) - val cameraChangeListener: OnCameraTrackingChangedListener = mock(OnCameraTrackingChangedListener::class.java) - locationComponent.addOnCameraTrackingChangedListener(cameraChangeListener) - - locationComponent.cameraTrackingChangedListener.onCameraTrackingChanged(CameraMode.TRACKING_GPS) - - verify(locationAnimatorCoordinator).cancelZoomAnimation() - verify(locationAnimatorCoordinator).cancelTiltAnimation() - verify(locationAnimatorCoordinator).updateAnimatorListenerHolders(eq(setOf(cameraValueListener, layerValueListener))) - verify(locationAnimatorCoordinator).resetAllCameraAnimations(any(), anyBoolean()) - verify(locationAnimatorCoordinator).resetAllLayerAnimations() - verify(cameraChangeListener).onCameraTrackingChanged(CameraMode.TRACKING_GPS) - } - - @Test - fun internal_renderModeChangedListener_onRenderModeChanged() { - locationComponent.activateLocationComponent(context, mock(Style::class.java), locationEngine, locationEngineRequest, locationComponentOptions) - locationComponent.isLocationComponentEnabled = true - - val cameraListener: AnimatorListenerHolder = mock(AnimatorListenerHolder::class.java) - val layerListener: AnimatorListenerHolder = mock(AnimatorListenerHolder::class.java) - `when`(locationCameraController.animationListeners).thenReturn(setOf(cameraListener)) - `when`(locationLayerController.animationListeners).thenReturn(setOf(layerListener)) - val renderChangeListener: OnRenderModeChangedListener = mock(OnRenderModeChangedListener::class.java) - locationComponent.addOnRenderModeChangedListener(renderChangeListener) - - locationComponent.renderModeChangedListener.onRenderModeChanged(RenderMode.NORMAL) - - verify(locationAnimatorCoordinator).updateAnimatorListenerHolders(eq(setOf(cameraListener, layerListener))) - verify(locationAnimatorCoordinator).resetAllCameraAnimations(any(), anyBoolean()) - verify(locationAnimatorCoordinator).resetAllLayerAnimations() - verify(renderChangeListener).onRenderModeChanged(RenderMode.NORMAL) - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java deleted file mode 100644 index ed2d015d8..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/LocationLayerControllerTest.java +++ /dev/null @@ -1,718 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import android.graphics.Bitmap; -import android.support.annotation.NonNull; - -import com.google.gson.JsonElement; -import com.mapbox.geojson.Feature; -import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.location.modes.RenderMode; -import com.mapbox.mapboxsdk.maps.MapboxMap; -import com.mapbox.mapboxsdk.maps.Style; -import com.mapbox.mapboxsdk.style.layers.Layer; -import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import java.util.Set; - -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.ACCURACY_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BACKGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.BEARING_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_LAYER; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.FOREGROUND_STALE_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.LOCATION_SOURCE; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_ACCURACY_RADIUS; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_FOREGROUND_ICON_OFFSET; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.PROPERTY_SHADOW_ICON_OFFSET; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_ICON; -import static com.mapbox.mapboxsdk.location.LocationComponentConstants.SHADOW_LAYER; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_ACCURACY; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_COMPASS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_GPS_BEARING; -import static com.mapbox.mapboxsdk.location.MapboxAnimator.ANIMATOR_LAYER_LATLNG; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class LocationLayerControllerTest { - - private MapboxMap mapboxMap = mock(MapboxMap.class); - private Style style = mock(Style.class); - - @Before - public void before() { - when(mapboxMap.getStyle()).thenReturn(style); - } - - @Test - public void onInitialization_locationSourceIsAdded() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addSource(locationSource); - } - - @Test - public void onInitialization_shadowLayerIsAdded() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - Layer shadowLayer = mock(Layer.class); - when(sourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(shadowLayer); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addLayerBelow(shadowLayer, BACKGROUND_LAYER); - } - - @Test - public void onInitialization_backgroundLayerIsAdded() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - Layer backgroundLayer = mock(Layer.class); - when(sourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(backgroundLayer); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addLayerBelow(backgroundLayer, FOREGROUND_LAYER); - } - - @Test - public void onInitialization_foregroundLayerIsAdded() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - Layer foregroundLayer = mock(Layer.class); - when(sourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(foregroundLayer); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addLayerBelow(foregroundLayer, BEARING_LAYER); - } - - @Test - public void onInitialization_bearingLayerIsAdded() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - Layer bearingLayer = mock(Layer.class); - when(sourceProvider.generateLayer(BEARING_LAYER)).thenReturn(bearingLayer); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - String layerBelow = "layer-below"; - when(options.layerBelow()).thenReturn(layerBelow); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addLayerBelow(bearingLayer, layerBelow); - } - - @Test - public void onInitialization_accuracyLayerIsAdded() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - Layer accuracyLayer = mock(Layer.class); - when(sourceProvider.generateAccuracyLayer()).thenReturn(accuracyLayer); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addLayerBelow(accuracyLayer, BACKGROUND_LAYER); - } - - @Test - public void onInitialization_numberOfCachedLayerIdsIsConstant() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - LocationLayerController controller = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - controller.initializeComponents(mapboxMap.getStyle(), options); - - assertEquals(5, controller.layerSet.size()); - } - - @Test - public void applyStyle_styleShadowWithValidElevation() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.elevation()).thenReturn(2f); - - // Style is applied on initialization - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(SHADOW_ICON, bitmap); - } - - @Test - public void applyStyle_ignoreStyleShadowWithInvalidElevation() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap); - LocationComponentOptions options = mock(LocationComponentOptions.class); - when(options.elevation()).thenReturn(0f); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style, times(0)).addImage(SHADOW_ICON, bitmap); - } - - @Test - public void applyStyle_styleForegroundFromOptions() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - int drawableResId = 123; - int tintColor = 456; - when(options.foregroundDrawable()).thenReturn(drawableResId); - when(options.foregroundTintColor()).thenReturn(tintColor); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(FOREGROUND_ICON, bitmap); - } - - @Test - public void applyStyle_styleForegroundStaleFromOptions() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - int drawableResId = 123; - int tintColor = 456; - when(options.foregroundDrawableStale()).thenReturn(drawableResId); - when(options.foregroundStaleTintColor()).thenReturn(tintColor); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(FOREGROUND_STALE_ICON, bitmap); - } - - @Test - public void applyStyle_styleBackgroundFromOptions() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - int drawableResId = 123; - int tintColor = 456; - when(options.backgroundDrawable()).thenReturn(drawableResId); - when(options.backgroundTintColor()).thenReturn(tintColor); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(BACKGROUND_ICON, bitmap); - } - - @Test - public void applyStyle_styleBackgroundStaleFromOptions() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - int drawableResId = 123; - int tintColor = 456; - when(options.backgroundDrawableStale()).thenReturn(drawableResId); - when(options.backgroundStaleTintColor()).thenReturn(tintColor); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(BACKGROUND_STALE_ICON, bitmap); - } - - @Test - public void applyStyle_styleBearingFromOptions() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - int drawableResId = 123; - int tintColor = 456; - when(options.bearingDrawable()).thenReturn(drawableResId); - when(options.bearingTintColor()).thenReturn(tintColor); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); - - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(BEARING_ICON, bitmap); - } - - @Test - public void applyStyle_layerBelowChanged() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - - LocationComponentOptions options = mock(LocationComponentOptions.class); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap); - - LocationLayerController layerController = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - Layer bearingLayer2 = mock(Layer.class); - when(sourceProvider.generateLayer(BEARING_LAYER)).thenReturn(bearingLayer2); - Layer foregroundLayer2 = mock(Layer.class); - when(sourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(foregroundLayer2); - Layer backgroundLayer2 = mock(Layer.class); - when(sourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(backgroundLayer2); - Layer shadowLayer2 = mock(Layer.class); - when(sourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(shadowLayer2); - Layer accuracyLayer2 = mock(Layer.class); - when(sourceProvider.generateAccuracyLayer()).thenReturn(accuracyLayer2); - String layerBelow = "layer-below"; - when(options.layerBelow()).thenReturn(layerBelow); - - layerController.applyStyle(options); - - verify(style).removeLayer(BEARING_LAYER); - verify(style).removeLayer(FOREGROUND_LAYER); - verify(style).removeLayer(BACKGROUND_LAYER); - verify(style).removeLayer(SHADOW_LAYER); - verify(style).removeLayer(ACCURACY_LAYER); - - verify(style).addLayerBelow(bearingLayer2, layerBelow); - verify(style).addLayerBelow(foregroundLayer2, BEARING_LAYER); - verify(style).addLayerBelow(backgroundLayer2, FOREGROUND_LAYER); - verify(style).addLayerBelow(shadowLayer2, BACKGROUND_LAYER); - verify(style).addLayerBelow(accuracyLayer2, BACKGROUND_LAYER); - } - - @Test - public void applyStyle_layerBelowNotChanged() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap); - - String layerBelow = "layer-below"; - when(options.layerBelow()).thenReturn(layerBelow); - - LocationLayerController layerController = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - options = mock(LocationComponentOptions.class); - layerBelow = "layer-below"; - when(options.layerBelow()).thenReturn(layerBelow); - layerController.applyStyle(options); - - verify(style, times(0)).removeLayer(any(String.class)); - verify(style, times(5)).addLayerBelow(any(Layer.class), any(String.class)); - } - - @Test - public void applyStyle_layerBelowNotChangedNull() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateShadowBitmap(any(LocationComponentOptions.class))).thenReturn(bitmap); - - LocationLayerController layerController = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - options = mock(LocationComponentOptions.class); - layerController.applyStyle(options); - - verify(style, times(0)).removeLayer(any(String.class)); - verify(style, times(1)).addLayer(any(Layer.class)); - verify(style, times(4)).addLayerBelow(any(Layer.class), Mockito.any()); - } - - @Test - public void updateForegroundOffset_foregroundIconPropertyIsUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - - layer.updateForegroundOffset(2d); - - verify(locationFeature).addProperty(eq(PROPERTY_FOREGROUND_ICON_OFFSET), any(JsonElement.class)); - } - - @Test - public void updateForegroundOffset_shadowPropertyIsUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.updateForegroundOffset(2d); - - verify(locationFeature).addProperty(eq(PROPERTY_SHADOW_ICON_OFFSET), any(JsonElement.class)); - } - - @Test - public void onNewLatLngValue_locationFeatureIsUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - - getAnimationListener(ANIMATOR_LAYER_LATLNG, layer.getAnimationListeners()).onNewAnimationValue(new LatLng()); - - // wanted twice (once for initialization) - verify(locationSource, times(2)).setGeoJson(locationFeature); - } - - @Test - public void onNewGpsBearingValue_locationFeatureIsUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.setRenderMode(RenderMode.GPS); - float gpsBearing = 2f; - - getAnimationListener(ANIMATOR_LAYER_GPS_BEARING, layer.getAnimationListeners()).onNewAnimationValue(gpsBearing); - - verify(locationFeature).addNumberProperty(PROPERTY_GPS_BEARING, gpsBearing); - } - - @Test - public void onNewGpsBearingValue_updateIgnoredWithInvalidRenderMode() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.setRenderMode(RenderMode.COMPASS); - float gpsBearing = 2f; - - assertNull(getAnimationListener(ANIMATOR_LAYER_GPS_BEARING, layer.getAnimationListeners())); - verify(locationFeature, times(0)).addNumberProperty(PROPERTY_GPS_BEARING, gpsBearing); - } - - @Test - public void onNewCompassBearingValue_locationFeatureIsUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.setRenderMode(RenderMode.COMPASS); - float compassBearing = 2f; - - getAnimationListener(ANIMATOR_LAYER_COMPASS_BEARING, layer.getAnimationListeners()) - .onNewAnimationValue(compassBearing); - - verify(locationFeature).addNumberProperty(PROPERTY_COMPASS_BEARING, compassBearing); - } - - @Test - public void onNewCompassBearingValue_updateIgnoredWithInvalidRenderMode() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.setRenderMode(RenderMode.GPS); - float compassBearing = 2f; - - assertNull(getAnimationListener(ANIMATOR_LAYER_COMPASS_BEARING, layer.getAnimationListeners())); - verify(locationFeature, times(0)).addNumberProperty(PROPERTY_COMPASS_BEARING, compassBearing); - } - - @Test - public void onNewAccuracyRadiusValue_locationFeatureIsUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.setRenderMode(RenderMode.NORMAL); - float accuracyRadiusValue = 2f; - - getAnimationListener(ANIMATOR_LAYER_ACCURACY, layer.getAnimationListeners()) - .onNewAnimationValue(accuracyRadiusValue); - - verify(locationFeature).addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracyRadiusValue); - } - - @Test - public void onNewAccuracyRadiusValue_updateIgnoredWithInvalidRenderMode() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(style.getSourceAs(LOCATION_SOURCE)).thenReturn(locationSource); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - Feature locationFeature = mock(Feature.class); - LocationLayerController layer = new LocationLayerController( - mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(locationFeature, options), - bitmapProvider, options, internalRenderModeChangedListener - ); - layer.setRenderMode(RenderMode.GPS); - float accuracyRadiusValue = 2f; - - assertNull(getAnimationListener(ANIMATOR_LAYER_ACCURACY, layer.getAnimationListeners())); - verify(locationFeature, times(0)) - .addNumberProperty(PROPERTY_ACCURACY_RADIUS, accuracyRadiusValue); - } - - @Test - public void renderModeChanged_doNotNotifyAboutDuplicates_NORMAL() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - LocationLayerController controller = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - controller.setRenderMode(RenderMode.NORMAL); - controller.setRenderMode(RenderMode.NORMAL); - - verify(internalRenderModeChangedListener, times(1)).onRenderModeChanged(RenderMode.NORMAL); - } - - @Test - public void renderModeChanged_doNotNotifyAboutDuplicates_GPS() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - GeoJsonSource locationSource = mock(GeoJsonSource.class); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(locationSource); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - LocationComponentOptions options = mock(LocationComponentOptions.class); - - LocationLayerController controller = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - controller.setRenderMode(RenderMode.GPS); - controller.setRenderMode(RenderMode.GPS); - - verify(internalRenderModeChangedListener, times(1)).onRenderModeChanged(RenderMode.GPS); - } - - @Test - public void layerHidden_renderModeChanged_layerShown_foregroundIconUpdated() { - OnRenderModeChangedListener internalRenderModeChangedListener = mock(OnRenderModeChangedListener.class); - LayerSourceProvider sourceProvider = buildLayerProvider(); - when(sourceProvider.generateSource(any(Feature.class))).thenReturn(mock(GeoJsonSource.class)); - LocationComponentOptions options = mock(LocationComponentOptions.class); - int drawableResId = 123; - int tintColor = 456; - when(options.foregroundDrawable()).thenReturn(drawableResId); - when(options.foregroundTintColor()).thenReturn(tintColor); - LayerBitmapProvider bitmapProvider = mock(LayerBitmapProvider.class); - Bitmap bitmap = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableResId, tintColor)).thenReturn(bitmap); - - LocationLayerController controller = - new LocationLayerController(mapboxMap, mapboxMap.getStyle(), sourceProvider, buildFeatureProvider(options), - bitmapProvider, options, internalRenderModeChangedListener); - - verify(style).addImage(FOREGROUND_ICON, bitmap); - - int drawableGpsResId = 789; - when(options.gpsDrawable()).thenReturn(drawableGpsResId); - - Bitmap bitmapGps = mock(Bitmap.class); - when(bitmapProvider.generateBitmap(drawableGpsResId, tintColor)).thenReturn(bitmapGps); - - controller.hide(); - - controller.setRenderMode(RenderMode.GPS); - - controller.show(); - - verify(style).addImage(FOREGROUND_ICON, bitmapGps); - } - - private LayerFeatureProvider buildFeatureProvider(@NonNull LocationComponentOptions options) { - LayerFeatureProvider provider = mock(LayerFeatureProvider.class); - when(provider.generateLocationFeature(null, options)).thenReturn(mock(Feature.class)); - return provider; - } - - private LayerFeatureProvider buildFeatureProvider(Feature feature, @NonNull LocationComponentOptions options) { - LayerFeatureProvider provider = mock(LayerFeatureProvider.class); - when(provider.generateLocationFeature(null, options)).thenReturn(feature); - return provider; - } - - private LayerSourceProvider buildLayerProvider() { - LayerSourceProvider layerSourceProvider = mock(LayerSourceProvider.class); - - Layer shadowLayer = mock(Layer.class); - when(shadowLayer.getId()).thenReturn(SHADOW_LAYER); - when(layerSourceProvider.generateLayer(SHADOW_LAYER)).thenReturn(shadowLayer); - - Layer backgroundLayer = mock(Layer.class); - when(backgroundLayer.getId()).thenReturn(BACKGROUND_LAYER); - when(layerSourceProvider.generateLayer(BACKGROUND_LAYER)).thenReturn(backgroundLayer); - - Layer foregroundLayer = mock(Layer.class); - when(foregroundLayer.getId()).thenReturn(FOREGROUND_LAYER); - when(layerSourceProvider.generateLayer(FOREGROUND_LAYER)).thenReturn(foregroundLayer); - - Layer bearingLayer = mock(Layer.class); - when(bearingLayer.getId()).thenReturn(BEARING_LAYER); - when(layerSourceProvider.generateLayer(BEARING_LAYER)).thenReturn(bearingLayer); - - Layer accuracyLayer = mock(Layer.class); - when(accuracyLayer.getId()).thenReturn(ACCURACY_LAYER); - when(layerSourceProvider.generateAccuracyLayer()).thenReturn(accuracyLayer); - return layerSourceProvider; - } - - private MapboxAnimator.AnimationsValueChangeListener getAnimationListener( - @MapboxAnimator.Type int animatorType, - Set holders) { - for (AnimatorListenerHolder holder : holders) { - @MapboxAnimator.Type int type = holder.getAnimatorType(); - if (type == animatorType) { - return holder.getListener(); - } - } - return null; - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt deleted file mode 100644 index 7c126c783..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/MapboxAnimatorTest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.mapbox.mapboxsdk.location - -import android.animation.ValueAnimator -import io.mockk.every -import io.mockk.mockk -import io.mockk.verify -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class MapboxAnimatorTest { - - @Test - fun fps_unlimited() { - val valueAnimator = mockk() - every { valueAnimator.animatedValue } answers { 5f } - val listener = mockk>() - every { listener.onNewAnimationValue(any()) } answers {} - val mapboxAnimator = MapboxFloatAnimator(0f, 10f, listener, Int.MAX_VALUE) - - for (i in 0 until 5) - mapboxAnimator.onAnimationUpdate(valueAnimator) - - verify(exactly = 5) { listener.onNewAnimationValue(5f) } - } - - @Test - fun fps_limited() { - val valueAnimator = mockk() - every { valueAnimator.animatedValue } answers { 5f } - val listener = mockk>() - every { listener.onNewAnimationValue(any()) } answers {} - val mapboxAnimator = MapboxFloatAnimator(0f, 10f, listener, 5) - - for (i in 0 until 5) { - mapboxAnimator.onAnimationUpdate(valueAnimator) - Thread.sleep(150) - } - - verify(exactly = 3) { listener.onNewAnimationValue(5f) } - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java deleted file mode 100644 index bf5ee40f4..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/location/UtilsTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.mapbox.mapboxsdk.location; - -import org.junit.Test; - -import static junit.framework.Assert.assertEquals; - -public final class UtilsTest { - - @Test - public void shortestRotation_doesReturnValueDistanceQuickestToZero() throws Exception { - float value = Utils.shortestRotation(0, 181); - assertEquals(360f, value); - value = Utils.shortestRotation(0, 179); - assertEquals(0f, value); - value = Utils.shortestRotation(0, 180); - assertEquals(0f, value); - } - - @Test - public void shortestRotation_doesReturnValueDistanceQuickestToFifty() throws Exception { - float value = Utils.shortestRotation(50, 231); - assertEquals(410f, value); - value = Utils.shortestRotation(50, 229); - assertEquals(50f, value); - value = Utils.shortestRotation(50, 180); - assertEquals(50f, value); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/log/LoggerTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/log/LoggerTest.kt deleted file mode 100644 index 82e1c67f6..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/log/LoggerTest.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.mapbox.mapboxsdk.log - -import io.mockk.mockk -import io.mockk.verify -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class LoggerTest { - - private val logger: LoggerDefinition = mockk(relaxed = true) - - @Before - fun setUp() { - Logger.setLoggerDefinition(logger) - } - - @Test - fun verbosityLogLevel() { - Logger.setVerbosity(Logger.VERBOSE) - Logger.v(TAG, MESSAGE) - verify { logger.v(TAG, MESSAGE) } - } - - @Test - fun verbosityLogLevelIgnore() { - Logger.setVerbosity(Logger.DEBUG) - Logger.v(TAG, MESSAGE) - verify(exactly = 0) { logger.v(TAG, MESSAGE) } - } - - @Test - fun debugLogLevel() { - Logger.setVerbosity(Logger.VERBOSE) - Logger.d(TAG, MESSAGE) - verify { logger.d(TAG, MESSAGE) } - } - - @Test - fun debugLogLevelIgnore() { - Logger.setVerbosity(Logger.WARN) - Logger.d(TAG, MESSAGE) - verify(exactly = 0) { logger.d(TAG, MESSAGE) } - } - - @Test - fun warnLogLevel() { - Logger.setVerbosity(Logger.WARN) - Logger.w(TAG, MESSAGE) - verify { logger.w(TAG, MESSAGE) } - } - - @Test - fun warnLogLevelIgnore() { - Logger.setVerbosity(Logger.ERROR) - Logger.w(TAG, MESSAGE) - verify(exactly = 0) { logger.w(TAG, MESSAGE) } - } - - @Test - fun errorLogLevel() { - Logger.setVerbosity(Logger.ERROR) - Logger.e(TAG, MESSAGE) - verify { logger.e(TAG, MESSAGE) } - } - - @Test - fun errorLogLevelIgnore() { - Logger.setVerbosity(Logger.NONE) - Logger.e(TAG, MESSAGE) - verify(exactly = 0) { logger.e(TAG, MESSAGE) } - } - - @Test - fun noneLogLevelIgnore() { - Logger.setVerbosity(Logger.NONE) - Logger.v(TAG, MESSAGE) - Logger.d(TAG, MESSAGE) - Logger.w(TAG, MESSAGE) - Logger.e(TAG, MESSAGE) - verify(exactly = 0) { logger.v(TAG, MESSAGE) } - verify(exactly = 0) { logger.d(TAG, MESSAGE) } - verify(exactly = 0) { logger.w(TAG, MESSAGE) } - verify(exactly = 0) { logger.e(TAG, MESSAGE) } - } - - companion object { - const val TAG: String = "TAG" - const val MESSAGE: String = "message" - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java deleted file mode 100644 index fd59f52a5..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AnnotationManagerTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.support.v4.util.LongSparseArray; - -import com.mapbox.mapboxsdk.annotations.Annotation; -import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions; -import com.mapbox.mapboxsdk.annotations.Marker; -import com.mapbox.mapboxsdk.annotations.MarkerOptions; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Test; -import org.mockito.ArgumentMatchers; - -import java.util.ArrayList; -import java.util.List; - -import static junit.framework.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class AnnotationManagerTest { - - @Test - public void checksAddAMarker() throws Exception { - NativeMap aNativeMapView = mock(NativeMapView.class); - MapView aMapView = mock(MapView.class); - LongSparseArray annotationsArray = new LongSparseArray<>(); - IconManager aIconManager = mock(IconManager.class); - Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray); - Markers markers = new MarkerContainer(aNativeMapView, annotationsArray, aIconManager); - Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray); - Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray); - ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(aNativeMapView, annotationsArray); - AnnotationManager annotationManager = new AnnotationManager(aMapView, annotationsArray, - aIconManager, annotations, markers, polygons, polylines, shapeAnnotations); - Marker aMarker = mock(Marker.class); - long aId = 5L; - when(aNativeMapView.addMarker(aMarker)).thenReturn(aId); - BaseMarkerOptions aMarkerOptions = mock(BaseMarkerOptions.class); - MapboxMap aMapboxMap = mock(MapboxMap.class); - when(aMarkerOptions.getMarker()).thenReturn(aMarker); - - annotationManager.addMarker(aMarkerOptions, aMapboxMap); - - assertEquals(aMarker, annotationManager.getAnnotations().get(0)); - assertEquals(aMarker, annotationManager.getAnnotation(aId)); - } - - @Test - public void checksAddMarkers() throws Exception { - NativeMapView aNativeMapView = mock(NativeMapView.class); - MapView aMapView = mock(MapView.class); - LongSparseArray annotationsArray = new LongSparseArray<>(); - IconManager aIconManager = mock(IconManager.class); - Annotations annotations = new AnnotationContainer(aNativeMapView, annotationsArray); - Markers markers = new MarkerContainer(aNativeMapView, annotationsArray, aIconManager); - Polygons polygons = new PolygonContainer(aNativeMapView, annotationsArray); - Polylines polylines = new PolylineContainer(aNativeMapView, annotationsArray); - ShapeAnnotations shapeAnnotations = new ShapeAnnotationContainer(aNativeMapView, annotationsArray); - AnnotationManager annotationManager = new AnnotationManager(aMapView, annotationsArray, - aIconManager, annotations, markers, polygons, polylines, shapeAnnotations); - - long firstId = 1L; - long secondId = 2L; - List markerList = new ArrayList<>(); - MarkerOptions firstMarkerOption = new MarkerOptions().position(new LatLng()).title("first"); - MarkerOptions secondMarkerOption = new MarkerOptions().position(new LatLng()).title("second"); - - markerList.add(firstMarkerOption); - markerList.add(secondMarkerOption); - MapboxMap aMapboxMap = mock(MapboxMap.class); - when(aNativeMapView.addMarker(any(Marker.class))).thenReturn(firstId, secondId); - - when(aNativeMapView.addMarkers(ArgumentMatchers.anyList())) - .thenReturn(new long[] {firstId, secondId}); - - annotationManager.addMarkers(markerList, aMapboxMap); - - assertEquals(2, annotationManager.getAnnotations().size()); - assertEquals("first", ((Marker) annotationManager.getAnnotations().get(0)).getTitle()); - assertEquals("second", ((Marker) annotationManager.getAnnotations().get(1)).getTitle()); - assertEquals("first", ((Marker) annotationManager.getAnnotation(firstId)).getTitle()); - assertEquals("second", ((Marker) annotationManager.getAnnotation(secondId)).getTitle()); - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AttributionDialogManagerTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AttributionDialogManagerTest.java deleted file mode 100644 index 9b4eeb27a..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/AttributionDialogManagerTest.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.content.Context; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.robolectric.RobolectricTestRunner; - -import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(RobolectricTestRunner.class) -public class AttributionDialogManagerTest { - @InjectMocks - Context context = mock(Context.class); - - @InjectMocks - MapboxMap mapboxMap = mock(MapboxMap.class); - - @InjectMocks - Style style = mock(Style.class); - - private AttributionDialogManager attributionDialogManager; - private CameraPosition cameraPosition; - - private static final String ASSERT_MAPBOX_TOKEN = "TestAccessToken"; - - private static final String ASSERT_MAPBOX_STYLE_URI = "mapbox://styles/mapbox/streets-v11"; - private static final String ASSERT_MAPBOX_LOCAL_STYLE_URI = "asset://style.json"; - - private static final String ASSERT_MAPBOX_PACKAGE_NAME = "com.mapbox.attributionmanagertest"; - - private static final String ASSERT_MAPBOX_FEEDBACK_FINAL_URL = - "https://apps.mapbox.com/feedback?referrer=com.mapbox.attributionmanagertest&" - + "access_token=TestAccessToken&owner=mapbox&id=streets-v11" - + "#/22.200001/11.100000/12.000000/24.000000/5"; - private static final String ASSERT_MAPBOX_FEEDHACK_FINAL_URL_LOCAL_STYLE = - "https://apps.mapbox.com/feedback?referrer=com.mapbox.attributionmanagertest&" - + "access_token=TestAccessToken#/22.200001/11.100000/12.000000/24.000000/5"; - private static final String ASSERT_MAPBOX_FEEDBACL_FINAL_URL_NULL_CAMERA_POSITION = - "https://apps.mapbox.com/feedback?referrer=com.mapbox.attributionmanagertest&access_token=TestAccessToken"; - - @Before - public void beforeTest() { - attributionDialogManager = new AttributionDialogManager(context, mapboxMap); - cameraPosition = new CameraPosition.Builder(CameraPosition.DEFAULT) - .tilt(5.0f).zoom(12).bearing(24.0f).target(new LatLng(11.1f, 22.2f)).build(); - } - - @Test - public void testSanity() { - assertNotNull("AttributionDialogManager should not be null", attributionDialogManager); - } - - @Test - public void testBuildMapFeedbackMapUrl() { - when(context.getApplicationContext()).thenReturn(context); - when(context.getPackageName()).thenReturn(ASSERT_MAPBOX_PACKAGE_NAME); - when(style.getUri()).thenReturn(ASSERT_MAPBOX_STYLE_URI); - when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition); - when(mapboxMap.getStyle()).thenReturn(style); - - Assert.assertEquals(ASSERT_MAPBOX_FEEDBACK_FINAL_URL, - attributionDialogManager.buildMapFeedbackMapUrl(ASSERT_MAPBOX_TOKEN)); - } - - @Test - public void testBuildMapFeedbackMapUrlWithLocalStyleJson() { - when(context.getApplicationContext()).thenReturn(context); - when(context.getPackageName()).thenReturn(ASSERT_MAPBOX_PACKAGE_NAME); - when(style.getUri()).thenReturn(ASSERT_MAPBOX_LOCAL_STYLE_URI); - when(mapboxMap.getCameraPosition()).thenReturn(cameraPosition); - when(mapboxMap.getStyle()).thenReturn(style); - - Assert.assertEquals(ASSERT_MAPBOX_FEEDHACK_FINAL_URL_LOCAL_STYLE, - attributionDialogManager.buildMapFeedbackMapUrl(ASSERT_MAPBOX_TOKEN)); - } - - @Test - public void testBuildMapFeedbackMapUrlWithNullCameraPosition() { - when(context.getApplicationContext()).thenReturn(context); - when(context.getPackageName()).thenReturn(ASSERT_MAPBOX_PACKAGE_NAME); - when(style.getUri()).thenReturn(ASSERT_MAPBOX_LOCAL_STYLE_URI); - when(mapboxMap.getCameraPosition()).thenReturn(null); - when(mapboxMap.getStyle()).thenReturn(style); - - Assert.assertEquals(ASSERT_MAPBOX_FEEDBACL_FINAL_URL_NULL_CAMERA_POSITION, - attributionDialogManager.buildMapFeedbackMapUrl(ASSERT_MAPBOX_TOKEN)); - } - - -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java deleted file mode 100644 index 025f06752..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java +++ /dev/null @@ -1,586 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import com.google.common.util.concurrent.ExecutionError; -import com.mapbox.mapboxsdk.log.Logger; -import com.mapbox.mapboxsdk.log.LoggerDefinition; - -import junit.framework.Assert; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.verify; - -/** - * Tests integration of MapChangeDispatcher and see if events are correctly forwarded. - */ -public class MapChangeReceiverTest { - - private static final String TEST_STRING = "mapChangeRandom"; - - private MapChangeReceiver mapChangeEventManager; - - @Mock - private MapView.OnCameraWillChangeListener onCameraWillChangeListener; - - @Mock - private MapView.OnCameraDidChangeListener onCameraDidChangeListener; - - @Mock - private MapView.OnCameraIsChangingListener onCameraIsChangingListener; - - @Mock - private MapView.OnWillStartLoadingMapListener onWillStartLoadingMapListener; - - @Mock - private MapView.OnDidFinishLoadingMapListener onDidFinishLoadingMapListener; - - @Mock - private MapView.OnDidFailLoadingMapListener onDidFailLoadingMapListener; - - @Mock - private MapView.OnWillStartRenderingFrameListener onWillStartRenderingFrameListener; - - @Mock - private MapView.OnDidFinishRenderingFrameListener onDidFinishRenderingFrameListener; - - @Mock - private MapView.OnWillStartRenderingMapListener onWillStartRenderingMapListener; - - @Mock - private MapView.OnDidFinishRenderingMapListener onDidFinishRenderingMapListener; - - @Mock - private MapView.OnDidBecomeIdleListener onDidBecomeIdleListener; - - @Mock - private MapView.OnDidFinishLoadingStyleListener onDidFinishLoadingStyleListener; - - @Mock - private MapView.OnSourceChangedListener onSourceChangedListener; - - @Mock - private LoggerDefinition loggerDefinition; - - @Before - public void beforeTest() { - MockitoAnnotations.initMocks(this); - mapChangeEventManager = new MapChangeReceiver(); - } - - @Test - public void testOnCameraRegionWillChangeListener() { - mapChangeEventManager.addOnCameraWillChangeListener(onCameraWillChangeListener); - mapChangeEventManager.onCameraWillChange(false); - verify(onCameraWillChangeListener).onCameraWillChange(false); - - mapChangeEventManager.removeOnCameraWillChangeListener(onCameraWillChangeListener); - mapChangeEventManager.onCameraWillChange(false); - verify(onCameraWillChangeListener).onCameraWillChange(false); - - mapChangeEventManager.addOnCameraWillChangeListener(onCameraWillChangeListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onCameraWillChangeListener).onCameraWillChange(false); - try { - mapChangeEventManager.onCameraWillChange(false); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onCameraWillChangeListener).onCameraWillChange(false); - try { - mapChangeEventManager.onCameraWillChange(false); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnCameraRegionWillChangeAnimatedListener() { - mapChangeEventManager.addOnCameraWillChangeListener(onCameraWillChangeListener); - mapChangeEventManager.onCameraWillChange(true); - verify(onCameraWillChangeListener).onCameraWillChange(true); - mapChangeEventManager.removeOnCameraWillChangeListener(onCameraWillChangeListener); - mapChangeEventManager.onCameraWillChange(true); - verify(onCameraWillChangeListener).onCameraWillChange(true); - - mapChangeEventManager.addOnCameraWillChangeListener(onCameraWillChangeListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onCameraWillChangeListener).onCameraWillChange(true); - try { - mapChangeEventManager.onCameraWillChange(true); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onCameraWillChangeListener).onCameraWillChange(true); - try { - mapChangeEventManager.onCameraWillChange(true); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnCameraIsChangingListener() { - mapChangeEventManager.addOnCameraIsChangingListener(onCameraIsChangingListener); - mapChangeEventManager.onCameraIsChanging(); - verify(onCameraIsChangingListener).onCameraIsChanging(); - mapChangeEventManager.removeOnCameraIsChangingListener(onCameraIsChangingListener); - mapChangeEventManager.onCameraIsChanging(); - verify(onCameraIsChangingListener).onCameraIsChanging(); - - mapChangeEventManager.addOnCameraIsChangingListener(onCameraIsChangingListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onCameraIsChangingListener).onCameraIsChanging(); - try { - mapChangeEventManager.onCameraIsChanging(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onCameraIsChangingListener).onCameraIsChanging(); - try { - mapChangeEventManager.onCameraIsChanging(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnCameraRegionDidChangeListener() { - mapChangeEventManager.addOnCameraDidChangeListener(onCameraDidChangeListener); - mapChangeEventManager.onCameraDidChange(false); - verify(onCameraDidChangeListener).onCameraDidChange(false); - mapChangeEventManager.removeOnCameraDidChangeListener(onCameraDidChangeListener); - mapChangeEventManager.onCameraDidChange(false); - verify(onCameraDidChangeListener).onCameraDidChange(false); - - mapChangeEventManager.addOnCameraDidChangeListener(onCameraDidChangeListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onCameraDidChangeListener).onCameraDidChange(false); - try { - mapChangeEventManager.onCameraDidChange(false); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onCameraDidChangeListener).onCameraDidChange(false); - try { - mapChangeEventManager.onCameraDidChange(false); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnCameraRegionDidChangeAnimatedListener() { - mapChangeEventManager.addOnCameraDidChangeListener(onCameraDidChangeListener); - mapChangeEventManager.onCameraDidChange(true); - verify(onCameraDidChangeListener).onCameraDidChange(true); - mapChangeEventManager.removeOnCameraDidChangeListener(onCameraDidChangeListener); - mapChangeEventManager.onCameraDidChange(true); - verify(onCameraDidChangeListener).onCameraDidChange(true); - - mapChangeEventManager.addOnCameraDidChangeListener(onCameraDidChangeListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onCameraDidChangeListener).onCameraDidChange(true); - try { - mapChangeEventManager.onCameraDidChange(true); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onCameraDidChangeListener).onCameraDidChange(true); - try { - mapChangeEventManager.onCameraDidChange(true); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnWillStartLoadingMapListener() { - mapChangeEventManager.addOnWillStartLoadingMapListener(onWillStartLoadingMapListener); - mapChangeEventManager.onWillStartLoadingMap(); - verify(onWillStartLoadingMapListener).onWillStartLoadingMap(); - mapChangeEventManager.removeOnWillStartLoadingMapListener(onWillStartLoadingMapListener); - mapChangeEventManager.onWillStartLoadingMap(); - verify(onWillStartLoadingMapListener).onWillStartLoadingMap(); - - mapChangeEventManager.addOnWillStartLoadingMapListener(onWillStartLoadingMapListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onWillStartLoadingMapListener).onWillStartLoadingMap(); - try { - mapChangeEventManager.onWillStartLoadingMap(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onWillStartLoadingMapListener).onWillStartLoadingMap(); - try { - mapChangeEventManager.onWillStartLoadingMap(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFinishLoadingMapListener() { - mapChangeEventManager.addOnDidFinishLoadingMapListener(onDidFinishLoadingMapListener); - mapChangeEventManager.onDidFinishLoadingMap(); - verify(onDidFinishLoadingMapListener).onDidFinishLoadingMap(); - mapChangeEventManager.removeOnDidFinishLoadingMapListener(onDidFinishLoadingMapListener); - mapChangeEventManager.onDidFinishLoadingMap(); - verify(onDidFinishLoadingMapListener).onDidFinishLoadingMap(); - - mapChangeEventManager.addOnDidFinishLoadingMapListener(onDidFinishLoadingMapListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFinishLoadingMapListener).onDidFinishLoadingMap(); - try { - mapChangeEventManager.onDidFinishLoadingMap(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFinishLoadingMapListener).onDidFinishLoadingMap(); - try { - mapChangeEventManager.onDidFinishLoadingMap(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFailLoadingMapListener() { - mapChangeEventManager.addOnDidFailLoadingMapListener(onDidFailLoadingMapListener); - mapChangeEventManager.onDidFailLoadingMap(TEST_STRING); - verify(onDidFailLoadingMapListener).onDidFailLoadingMap(TEST_STRING); - mapChangeEventManager.removeOnDidFailLoadingMapListener(onDidFailLoadingMapListener); - mapChangeEventManager.onDidFailLoadingMap(TEST_STRING); - verify(onDidFailLoadingMapListener).onDidFailLoadingMap(TEST_STRING); - - mapChangeEventManager.addOnDidFailLoadingMapListener(onDidFailLoadingMapListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFailLoadingMapListener).onDidFailLoadingMap(TEST_STRING); - try { - mapChangeEventManager.onDidFailLoadingMap(TEST_STRING); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFailLoadingMapListener).onDidFailLoadingMap(TEST_STRING); - try { - mapChangeEventManager.onDidFailLoadingMap(TEST_STRING); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnWillStartRenderingFrameListener() { - mapChangeEventManager.addOnWillStartRenderingFrameListener(onWillStartRenderingFrameListener); - mapChangeEventManager.onWillStartRenderingFrame(); - verify(onWillStartRenderingFrameListener).onWillStartRenderingFrame(); - mapChangeEventManager.removeOnWillStartRenderingFrameListener(onWillStartRenderingFrameListener); - mapChangeEventManager.onWillStartRenderingFrame(); - verify(onWillStartRenderingFrameListener).onWillStartRenderingFrame(); - - mapChangeEventManager.addOnWillStartRenderingFrameListener(onWillStartRenderingFrameListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onWillStartRenderingFrameListener).onWillStartRenderingFrame(); - try { - mapChangeEventManager.onWillStartRenderingFrame(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onWillStartRenderingFrameListener).onWillStartRenderingFrame(); - try { - mapChangeEventManager.onWillStartRenderingFrame(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFinishRenderingFrameListener() { - mapChangeEventManager.addOnDidFinishRenderingFrameListener(onDidFinishRenderingFrameListener); - mapChangeEventManager.onDidFinishRenderingFrame(true); - verify(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(true); - mapChangeEventManager.removeOnDidFinishRenderingFrameListener(onDidFinishRenderingFrameListener); - mapChangeEventManager.onDidFinishRenderingFrame(true); - verify(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(true); - - mapChangeEventManager.addOnDidFinishRenderingFrameListener(onDidFinishRenderingFrameListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(true); - try { - mapChangeEventManager.onDidFinishRenderingFrame(true); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(true); - try { - mapChangeEventManager.onDidFinishRenderingFrame(true); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFinishRenderingFrameFullyRenderedListener() { - mapChangeEventManager.addOnDidFinishRenderingFrameListener(onDidFinishRenderingFrameListener); - mapChangeEventManager.onDidFinishRenderingFrame(false); - verify(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(false); - mapChangeEventManager.removeOnDidFinishRenderingFrameListener(onDidFinishRenderingFrameListener); - mapChangeEventManager.onDidFinishRenderingFrame(false); - verify(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(false); - - mapChangeEventManager.addOnDidFinishRenderingFrameListener(onDidFinishRenderingFrameListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(false); - try { - mapChangeEventManager.onDidFinishRenderingFrame(false); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFinishRenderingFrameListener).onDidFinishRenderingFrame(false); - try { - mapChangeEventManager.onDidFinishRenderingFrame(false); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnWillStartRenderingMapListener() { - mapChangeEventManager.addOnWillStartRenderingMapListener(onWillStartRenderingMapListener); - mapChangeEventManager.onWillStartRenderingMap(); - verify(onWillStartRenderingMapListener).onWillStartRenderingMap(); - mapChangeEventManager.removeOnWillStartRenderingMapListener(onWillStartRenderingMapListener); - mapChangeEventManager.onWillStartRenderingMap(); - verify(onWillStartRenderingMapListener).onWillStartRenderingMap(); - - mapChangeEventManager.addOnWillStartRenderingMapListener(onWillStartRenderingMapListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onWillStartRenderingMapListener).onWillStartRenderingMap(); - try { - mapChangeEventManager.onWillStartRenderingMap(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onWillStartRenderingMapListener).onWillStartRenderingMap(); - try { - mapChangeEventManager.onWillStartRenderingMap(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFinishRenderingMapListener() { - mapChangeEventManager.addOnDidFinishRenderingMapListener(onDidFinishRenderingMapListener); - mapChangeEventManager.onDidFinishRenderingMap(true); - verify(onDidFinishRenderingMapListener).onDidFinishRenderingMap(true); - mapChangeEventManager.removeOnDidFinishRenderingMapListener(onDidFinishRenderingMapListener); - mapChangeEventManager.onDidFinishRenderingMap(true); - verify(onDidFinishRenderingMapListener).onDidFinishRenderingMap(true); - - mapChangeEventManager.addOnDidFinishRenderingMapListener(onDidFinishRenderingMapListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFinishRenderingMapListener).onDidFinishRenderingMap(true); - try { - mapChangeEventManager.onDidFinishRenderingMap(true); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFinishRenderingMapListener).onDidFinishRenderingMap(true); - try { - mapChangeEventManager.onDidFinishRenderingMap(true); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFinishRenderingMapFullyRenderedListener() { - mapChangeEventManager.addOnDidFinishRenderingMapListener(onDidFinishRenderingMapListener); - mapChangeEventManager.onDidFinishRenderingMap(false); - verify(onDidFinishRenderingMapListener).onDidFinishRenderingMap(false); - mapChangeEventManager.removeOnDidFinishRenderingMapListener(onDidFinishRenderingMapListener); - mapChangeEventManager.onDidFinishRenderingMap(false); - verify(onDidFinishRenderingMapListener).onDidFinishRenderingMap(false); - - mapChangeEventManager.addOnDidFinishRenderingMapListener(onDidFinishRenderingMapListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFinishRenderingMapListener).onDidFinishRenderingMap(false); - try { - mapChangeEventManager.onDidFinishRenderingMap(false); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFinishRenderingMapListener).onDidFinishRenderingMap(false); - try { - mapChangeEventManager.onDidFinishRenderingMap(false); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidBecomeIdleListener() { - mapChangeEventManager.addOnDidBecomeIdleListener(onDidBecomeIdleListener); - mapChangeEventManager.onDidBecomeIdle(); - verify(onDidBecomeIdleListener).onDidBecomeIdle(); - mapChangeEventManager.removeOnDidBecomeIdleListener(onDidBecomeIdleListener); - mapChangeEventManager.onDidBecomeIdle(); - verify(onDidBecomeIdleListener).onDidBecomeIdle(); - - mapChangeEventManager.addOnDidBecomeIdleListener(onDidBecomeIdleListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidBecomeIdleListener).onDidBecomeIdle(); - try { - mapChangeEventManager.onDidBecomeIdle(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidBecomeIdleListener).onDidBecomeIdle(); - try { - mapChangeEventManager.onDidBecomeIdle(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnDidFinishLoadingStyleListener() { - mapChangeEventManager.addOnDidFinishLoadingStyleListener(onDidFinishLoadingStyleListener); - mapChangeEventManager.onDidFinishLoadingStyle(); - verify(onDidFinishLoadingStyleListener).onDidFinishLoadingStyle(); - mapChangeEventManager.removeOnDidFinishLoadingStyleListener(onDidFinishLoadingStyleListener); - mapChangeEventManager.onDidFinishLoadingStyle(); - verify(onDidFinishLoadingStyleListener).onDidFinishLoadingStyle(); - - mapChangeEventManager.addOnDidFinishLoadingStyleListener(onDidFinishLoadingStyleListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onDidFinishLoadingStyleListener).onDidFinishLoadingStyle(); - try { - mapChangeEventManager.onDidFinishLoadingStyle(); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onDidFinishLoadingStyleListener).onDidFinishLoadingStyle(); - try { - mapChangeEventManager.onDidFinishLoadingStyle(); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } - - @Test - public void testOnSourceChangedListener() { - mapChangeEventManager.addOnSourceChangedListener(onSourceChangedListener); - mapChangeEventManager.onSourceChanged(TEST_STRING); - verify(onSourceChangedListener).onSourceChangedListener(TEST_STRING); - mapChangeEventManager.removeOnSourceChangedListener(onSourceChangedListener); - mapChangeEventManager.onSourceChanged(TEST_STRING); - verify(onSourceChangedListener).onSourceChangedListener(TEST_STRING); - - mapChangeEventManager.addOnSourceChangedListener(onSourceChangedListener); - Logger.setLoggerDefinition(loggerDefinition); - Exception exc = new RuntimeException(); - doThrow(exc).when(onSourceChangedListener).onSourceChangedListener(TEST_STRING); - try { - mapChangeEventManager.onSourceChanged(TEST_STRING); - Assert.fail("The exception should've been re-thrown."); - } catch (RuntimeException throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(exc)); - } - - Error err = new ExecutionError("", new Error()); - doThrow(err).when(onSourceChangedListener).onSourceChangedListener(TEST_STRING); - try { - mapChangeEventManager.onSourceChanged(TEST_STRING); - Assert.fail("The exception should've been re-thrown."); - } catch (ExecutionError throwable) { - verify(loggerDefinition).e(anyString(), anyString(), eq(err)); - } - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java deleted file mode 100644 index cb097c283..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapTouchListenersTest.java +++ /dev/null @@ -1,157 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.PointF; - -import android.support.annotation.Nullable; -import com.mapbox.android.gestures.MoveGestureDetector; -import com.mapbox.android.gestures.RotateGestureDetector; -import com.mapbox.android.gestures.ShoveGestureDetector; -import com.mapbox.android.gestures.StandardScaleGestureDetector; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Before; -import org.junit.Test; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class MapTouchListenersTest { - - @Nullable - private MapGestureDetector mapGestureDetector; - private LatLng latLng; - private PointF pointF; - - @Before - public void setUp() throws Exception { - latLng = new LatLng(); - pointF = new PointF(); - - Projection projection = mock(Projection.class); - when(projection.fromScreenLocation(pointF)).thenReturn(latLng); - mapGestureDetector = new MapGestureDetector(null, - null, projection, null, null, null); - } - - @Test - public void onMapClickListenerTest() throws Exception { - MapboxMap.OnMapClickListener listener = mock(MapboxMap.OnMapClickListener.class); - mapGestureDetector.addOnMapClickListener(listener); - mapGestureDetector.notifyOnMapClickListeners(pointF); - verify(listener, times(1)).onMapClick(latLng); - - mapGestureDetector.removeOnMapClickListener(listener); - mapGestureDetector.notifyOnMapClickListeners(pointF); - verify(listener, times(1)).onMapClick(latLng); - } - - @Test - public void onMapLongClickListenerTest() throws Exception { - MapboxMap.OnMapLongClickListener listener = mock(MapboxMap.OnMapLongClickListener.class); - mapGestureDetector.addOnMapLongClickListener(listener); - mapGestureDetector.notifyOnMapLongClickListeners(pointF); - verify(listener, times(1)).onMapLongClick(latLng); - - mapGestureDetector.removeOnMapLongClickListener(listener); - mapGestureDetector.notifyOnMapLongClickListeners(pointF); - verify(listener, times(1)).onMapLongClick(latLng); - } - - @Test - public void onFlingListenerTest() throws Exception { - MapboxMap.OnFlingListener listener = mock(MapboxMap.OnFlingListener.class); - mapGestureDetector.addOnFlingListener(listener); - mapGestureDetector.notifyOnFlingListeners(); - verify(listener, times(1)).onFling(); - - mapGestureDetector.removeOnFlingListener(listener); - mapGestureDetector.notifyOnFlingListeners(); - verify(listener, times(1)).onFling(); - } - - @Test - public void onMoveListenerTest() throws Exception { - MapboxMap.OnMoveListener listener = mock(MapboxMap.OnMoveListener.class); - MoveGestureDetector detector = mock(MoveGestureDetector.class); - mapGestureDetector.addOnMoveListener(listener); - mapGestureDetector.notifyOnMoveBeginListeners(detector); - mapGestureDetector.notifyOnMoveListeners(detector); - mapGestureDetector.notifyOnMoveEndListeners(detector); - verify(listener, times(1)).onMoveBegin(detector); - verify(listener, times(1)).onMove(detector); - verify(listener, times(1)).onMoveEnd(detector); - - mapGestureDetector.removeOnMoveListener(listener); - mapGestureDetector.notifyOnMoveBeginListeners(detector); - mapGestureDetector.notifyOnMoveListeners(detector); - mapGestureDetector.notifyOnMoveEndListeners(detector); - verify(listener, times(1)).onMoveBegin(detector); - verify(listener, times(1)).onMove(detector); - verify(listener, times(1)).onMoveEnd(detector); - } - - @Test - public void onRotateListenerTest() throws Exception { - MapboxMap.OnRotateListener listener = mock(MapboxMap.OnRotateListener.class); - RotateGestureDetector detector = mock(RotateGestureDetector.class); - mapGestureDetector.addOnRotateListener(listener); - mapGestureDetector.notifyOnRotateBeginListeners(detector); - mapGestureDetector.notifyOnRotateListeners(detector); - mapGestureDetector.notifyOnRotateEndListeners(detector); - verify(listener, times(1)).onRotateBegin(detector); - verify(listener, times(1)).onRotate(detector); - verify(listener, times(1)).onRotateEnd(detector); - - mapGestureDetector.removeOnRotateListener(listener); - mapGestureDetector.notifyOnRotateBeginListeners(detector); - mapGestureDetector.notifyOnRotateListeners(detector); - mapGestureDetector.notifyOnRotateEndListeners(detector); - verify(listener, times(1)).onRotateBegin(detector); - verify(listener, times(1)).onRotate(detector); - verify(listener, times(1)).onRotateEnd(detector); - } - - @Test - public void onScaleListenerTest() throws Exception { - MapboxMap.OnScaleListener listener = mock(MapboxMap.OnScaleListener.class); - StandardScaleGestureDetector detector = mock(StandardScaleGestureDetector.class); - mapGestureDetector.addOnScaleListener(listener); - mapGestureDetector.notifyOnScaleBeginListeners(detector); - mapGestureDetector.notifyOnScaleListeners(detector); - mapGestureDetector.notifyOnScaleEndListeners(detector); - verify(listener, times(1)).onScaleBegin(detector); - verify(listener, times(1)).onScale(detector); - verify(listener, times(1)).onScaleEnd(detector); - - mapGestureDetector.removeOnScaleListener(listener); - mapGestureDetector.notifyOnScaleBeginListeners(detector); - mapGestureDetector.notifyOnScaleListeners(detector); - mapGestureDetector.notifyOnScaleEndListeners(detector); - verify(listener, times(1)).onScaleBegin(detector); - verify(listener, times(1)).onScale(detector); - verify(listener, times(1)).onScaleEnd(detector); - } - - @Test - public void onShoveListenerTest() throws Exception { - MapboxMap.OnShoveListener listener = mock(MapboxMap.OnShoveListener.class); - ShoveGestureDetector detector = mock(ShoveGestureDetector.class); - mapGestureDetector.addShoveListener(listener); - mapGestureDetector.notifyOnShoveBeginListeners(detector); - mapGestureDetector.notifyOnShoveListeners(detector); - mapGestureDetector.notifyOnShoveEndListeners(detector); - verify(listener, times(1)).onShoveBegin(detector); - verify(listener, times(1)).onShove(detector); - verify(listener, times(1)).onShoveEnd(detector); - - mapGestureDetector.removeShoveListener(listener); - mapGestureDetector.notifyOnShoveBeginListeners(detector); - mapGestureDetector.notifyOnShoveListeners(detector); - mapGestureDetector.notifyOnShoveEndListeners(detector); - verify(listener, times(1)).onShoveBegin(detector); - verify(listener, times(1)).onShove(detector); - verify(listener, times(1)).onShoveEnd(detector); - } -} diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt deleted file mode 100644 index ee8024257..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsAttrsTest.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.mapbox.mapboxsdk.maps - -import android.content.Context -import android.content.res.Resources -import android.content.res.TypedArray -import com.mapbox.mapboxsdk.R -import io.mockk.MockKAnnotations -import io.mockk.every -import io.mockk.impl.annotations.RelaxedMockK -import io.mockk.verify -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class MapboxMapOptionsAttrsTest { - - @RelaxedMockK - private lateinit var options: MapboxMapOptions - - @RelaxedMockK - private lateinit var typedArray: TypedArray - - @RelaxedMockK - private lateinit var context: Context - - @RelaxedMockK - private lateinit var resources: Resources - - @Before - fun setUp() { - MockKAnnotations.init(this) - every { - context.resources - }.returns(resources) - } - - @Test - fun enabledLocalIdeographFontFamily() { - mockEnableLocalIdeograph(enabled = true) - - val options = MapboxMapOptions.createFromAttributes(options, context, typedArray) - - verify(exactly = 1) { - options.localIdeographFontFamily(any()) - } - } - - @Test - fun localIdeographFontFamily() { - mockEnableLocalIdeograph(enabled = true) - - val font = "foo" - mockLocalIdeographString(font) - - val options = MapboxMapOptions.createFromAttributes(options, context, typedArray) - - verify(exactly = 1) { - options.localIdeographFontFamily(font) - } - } - - @Test - fun localIdeographFontFamilies() { - mockEnableLocalIdeograph(enabled = true) - - val fonts = arrayOf("foo", "bar") - mockLocalIdeographStringArray(fonts) - - val options = MapboxMapOptions.createFromAttributes(options, context, typedArray) - - verify(exactly = 1) { - options.localIdeographFontFamily(*fonts) - } - } - - private fun mockEnableLocalIdeograph(enabled: Boolean) { - every { - typedArray.getBoolean(R.styleable.mapbox_MapView_mapbox_localIdeographEnabled, true) - }.returns(enabled) - } - - private fun mockLocalIdeographString(font: String) { - every { - typedArray.getString(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamily) - }.returns(font) - } - - private fun mockLocalIdeographStringArray(fonts: Array) { - val resId = 9000 - - every { - typedArray.getResourceId(R.styleable.mapbox_MapView_mapbox_localIdeographFontFamilies, 0) - }.returns(resId) - - every { - resources.getStringArray(resId) - }.returns(fonts) - } -} \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java deleted file mode 100644 index c46e6e319..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapOptionsTest.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.mapbox.mapboxsdk.maps; - -import android.graphics.Color; -import android.view.Gravity; - -import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.geometry.LatLng; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -import java.util.Arrays; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNull; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -@RunWith(RobolectricTestRunner.class) -public class MapboxMapOptionsTest { - - private static final double DELTA = 1e-15; - - @Test - public void testSanity() { - assertNotNull("should not be null", new MapboxMapOptions()); - } - - @Test - public void testDebugEnabled() { - assertFalse(new MapboxMapOptions().getDebugActive()); - assertTrue(new MapboxMapOptions().debugActive(true).getDebugActive()); - assertFalse(new MapboxMapOptions().debugActive(false).getDebugActive()); - } - - @Test - public void testCompassEnabled() { - assertTrue(new MapboxMapOptions().compassEnabled(true).getCompassEnabled()); - assertFalse(new MapboxMapOptions().compassEnabled(false).getCompassEnabled()); - } - - @Test - public void testCompassGravity() { - assertEquals(Gravity.TOP | Gravity.END, new MapboxMapOptions().getCompassGravity()); - assertEquals(Gravity.BOTTOM, new MapboxMapOptions().compassGravity(Gravity.BOTTOM).getCompassGravity()); - assertNotEquals(Gravity.START, new MapboxMapOptions().compassGravity(Gravity.BOTTOM).getCompassGravity()); - } - - @Test - public void testCompassMargins() { - assertTrue(Arrays.equals(new int[] {0, 1, 2, 3}, new MapboxMapOptions().compassMargins( - new int[] {0, 1, 2, 3}).getCompassMargins())); - assertFalse(Arrays.equals(new int[] {0, 1, 2, 3}, new MapboxMapOptions().compassMargins( - new int[] {0, 0, 0, 0}).getCompassMargins())); - } - - @Test - public void testLogoEnabled() { - assertTrue(new MapboxMapOptions().logoEnabled(true).getLogoEnabled()); - assertFalse(new MapboxMapOptions().logoEnabled(false).getLogoEnabled()); - } - - @Test - public void testLogoGravity() { - assertEquals(Gravity.BOTTOM | Gravity.START, new MapboxMapOptions().getLogoGravity()); - assertEquals(Gravity.BOTTOM, new MapboxMapOptions().logoGravity(Gravity.BOTTOM).getLogoGravity()); - assertNotEquals(Gravity.START, new MapboxMapOptions().logoGravity(Gravity.BOTTOM).getLogoGravity()); - } - - @Test - public void testLogoMargins() { - assertTrue(Arrays.equals(new int[] {0, 1, 2, 3}, new MapboxMapOptions().logoMargins( - new int[] {0, 1, 2, 3}).getLogoMargins())); - assertFalse(Arrays.equals(new int[] {0, 1, 2, 3}, new MapboxMapOptions().logoMargins( - new int[] {0, 0, 0, 0}).getLogoMargins())); - } - - @Test - public void testAttributionTintColor() { - assertEquals(-1, new MapboxMapOptions().getAttributionTintColor()); - assertEquals(Color.RED, new MapboxMapOptions().attributionTintColor(Color.RED).getAttributionTintColor()); - } - - @Test - public void testAttributionEnabled() { - assertTrue(new MapboxMapOptions().attributionEnabled(true).getAttributionEnabled()); - assertFalse(new MapboxMapOptions().attributionEnabled(false).getAttributionEnabled()); - } - - @Test - public void testAttributionGravity() { - assertEquals(Gravity.BOTTOM | Gravity.START, new MapboxMapOptions().getAttributionGravity()); - assertEquals(Gravity.BOTTOM, new MapboxMapOptions().attributionGravity(Gravity.BOTTOM).getAttributionGravity()); - assertNotEquals(Gravity.START, new MapboxMapOptions().attributionGravity(Gravity.BOTTOM).getAttributionGravity()); - } - - @Test - public void testAttributionMargins() { - assertTrue(Arrays.equals(new int[] {0, 1, 2, 3}, new MapboxMapOptions().attributionMargins( - new int[] {0, 1, 2, 3}).getAttributionMargins())); - assertFalse(Arrays.equals(new int[] {0, 1, 2, 3}, new MapboxMapOptions().attributionMargins( - new int[] {0, 0, 0, 0}).getAttributionMargins())); - } - - @Test - public void testMinZoom() { - assertEquals(MapboxConstants.MINIMUM_ZOOM, new MapboxMapOptions().getMinZoomPreference(), DELTA); - assertEquals(5.0f, new MapboxMapOptions().minZoomPreference(5.0f).getMinZoomPreference(), DELTA); - assertNotEquals(2.0f, new MapboxMapOptions().minZoomPreference(5.0f).getMinZoomPreference(), DELTA); - } - - @Test - public void testMaxZoom() { - assertEquals(MapboxConstants.MAXIMUM_ZOOM, new MapboxMapOptions().getMaxZoomPreference(), DELTA); - assertEquals(5.0f, new MapboxMapOptions().maxZoomPreference(5.0f).getMaxZoomPreference(), DELTA); - assertNotEquals(2.0f, new MapboxMapOptions().maxZoomPreference(5.0f).getMaxZoomPreference(), DELTA); - } - - @Test - public void testTiltGesturesEnabled() { - assertTrue(new MapboxMapOptions().getTiltGesturesEnabled()); - assertTrue(new MapboxMapOptions().tiltGesturesEnabled(true).getTiltGesturesEnabled()); - assertFalse(new MapboxMapOptions().tiltGesturesEnabled(false).getTiltGesturesEnabled()); - } - - @Test - public void testScrollGesturesEnabled() { - assertTrue(new MapboxMapOptions().getScrollGesturesEnabled()); - assertTrue(new MapboxMapOptions().scrollGesturesEnabled(true).getScrollGesturesEnabled()); - assertFalse(new MapboxMapOptions().scrollGesturesEnabled(false).getScrollGesturesEnabled()); - } - - @Test - public void testZoomGesturesEnabled() { - assertTrue(new MapboxMapOptions().getZoomGesturesEnabled()); - assertTrue(new MapboxMapOptions().zoomGesturesEnabled(true).getZoomGesturesEnabled()); - assertFalse(new MapboxMapOptions().zoomGesturesEnabled(false).getZoomGesturesEnabled()); - } - - @Test - public void testRotateGesturesEnabled() { - assertTrue(new MapboxMapOptions().getRotateGesturesEnabled()); - assertTrue(new MapboxMapOptions().rotateGesturesEnabled(true).getRotateGesturesEnabled()); - assertFalse(new MapboxMapOptions().rotateGesturesEnabled(false).getRotateGesturesEnabled()); - } - - @Test - public void testCamera() { - CameraPosition position = new CameraPosition.Builder().build(); - assertEquals(new CameraPosition.Builder(position).build(), new MapboxMapOptions().camera(position).getCamera()); - assertNotEquals(new CameraPosition.Builder().target(new LatLng(1, 1)), new MapboxMapOptions().camera(position)); - assertNull(new MapboxMapOptions().getCamera()); - } - - @Test - public void testPrefetchesTiles() { - // Default value - assertTrue(new MapboxMapOptions().getPrefetchesTiles()); - - // Check mutations - assertTrue(new MapboxMapOptions().setPrefetchesTiles(true).getPrefetchesTiles()); - assertFalse(new MapboxMapOptions().setPrefetchesTiles(false).getPrefetchesTiles()); - } - - @Test - public void testPrefetchZoomDelta() { - // Default value - assertEquals(4, new MapboxMapOptions().getPrefetchZoomDelta()); - - // Check mutations - assertEquals(5, new MapboxMapOptions().setPrefetchZoomDelta(5).getPrefetchZoomDelta()); - } - - - @Test - public void testCrossSourceCollisions() { - // Default value - assertTrue(new MapboxMapOptions().getCrossSourceCollisions()); - - // check mutations - assertTrue(new MapboxMapOptions().crossSourceCollisions(true).getCrossSourceCollisions()); - assertFalse(new MapboxMapOptions().crossSourceCollisions(false).getCrossSourceCollisions()); - } - - @Test - public void testLocalIdeographFontFamily_enabledByDefault() { - MapboxMapOptions options = MapboxMapOptions.createFromAttributes(RuntimeEnvironment.application, null); - assertEquals(MapboxConstants.DEFAULT_FONT, options.getLocalIdeographFontFamily()); - } -} - diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.kt b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.kt deleted file mode 100644 index 6647fe559..000000000 --- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapboxMapTest.kt +++ /dev/null @@ -1,214 +0,0 @@ -package com.mapbox.mapboxsdk.maps - -import com.mapbox.mapboxsdk.camera.CameraPosition -import com.mapbox.mapboxsdk.camera.CameraUpdateFactory -import com.mapbox.mapboxsdk.constants.MapboxConstants -import com.mapbox.mapboxsdk.geometry.LatLng -import com.mapbox.mapboxsdk.geometry.LatLngBounds -import com.mapbox.mapboxsdk.style.layers.TransitionOptions -import io.mockk.* -import junit.framework.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class MapboxMapTest { - - private lateinit var mapboxMap: MapboxMap - - private lateinit var nativeMapView: NativeMap - - private lateinit var transform: Transform - - private lateinit var cameraChangeDispatcher: CameraChangeDispatcher - - private lateinit var developerAnimationListener: MapboxMap.OnDeveloperAnimationListener - - @Before - fun setup() { - cameraChangeDispatcher = spyk() - developerAnimationListener = mockk(relaxed = true) - nativeMapView = mockk(relaxed = true) - transform = mockk(relaxed = true) - mapboxMap = MapboxMap(nativeMapView, transform, mockk(relaxed = true), null, null, cameraChangeDispatcher, listOf(developerAnimationListener)) - every { nativeMapView.isDestroyed } returns false - every { nativeMapView.nativePtr } returns 5 - mapboxMap.injectLocationComponent(spyk()) - mapboxMap.setStyle(Style.MAPBOX_STREETS) - mapboxMap.onFinishLoadingStyle() - } - - @Test - fun testTransitionOptions() { - val expected = TransitionOptions(100, 200) - mapboxMap.style?.transition = expected - verify { nativeMapView.transitionOptions = expected } - } - - @Test - fun testMoveCamera() { - val callback = mockk() - val target = LatLng(1.0, 2.0) - val expected = CameraPosition.Builder().target(target).build() - val update = CameraUpdateFactory.newCameraPosition(expected) - mapboxMap.moveCamera(update, callback) - verify { transform.moveCamera(mapboxMap, update, callback) } - verify { developerAnimationListener.onDeveloperAnimationStarted() } - } - - @Test - fun testEaseCamera() { - val callback = mockk() - val target = LatLng(1.0, 2.0) - val expected = CameraPosition.Builder().target(target).build() - val update = CameraUpdateFactory.newCameraPosition(expected) - mapboxMap.easeCamera(update, callback) - verify { transform.easeCamera(mapboxMap, update, MapboxConstants.ANIMATION_DURATION, true, callback) } - verify { developerAnimationListener.onDeveloperAnimationStarted() } - } - - @Test - fun testAnimateCamera() { - val callback = mockk() - val target = LatLng(1.0, 2.0) - val expected = CameraPosition.Builder().target(target).build() - val update = CameraUpdateFactory.newCameraPosition(expected) - mapboxMap.animateCamera(update, callback) - verify { transform.animateCamera(mapboxMap, update, MapboxConstants.ANIMATION_DURATION, callback) } - verify { developerAnimationListener.onDeveloperAnimationStarted() } - } - - @Test - fun testScrollBy() { - mapboxMap.scrollBy(100f, 200f) - verify { nativeMapView.moveBy(100.0, 200.0, 0) } - verify { developerAnimationListener.onDeveloperAnimationStarted() } - } - - @Test - fun testResetNorth() { - mapboxMap.resetNorth() - verify { transform.resetNorth() } - verify { developerAnimationListener.onDeveloperAnimationStarted() } - } - - @Test - fun testFocalBearing() { - mapboxMap.setFocalBearing(35.0, 100f, 200f, 1000) - verify { transform.setBearing(35.0, 100f, 200f, 1000) } - verify { developerAnimationListener.onDeveloperAnimationStarted() } - } - - @Test - fun testMinZoom() { - mapboxMap.setMinZoomPreference(10.0) - verify { transform.minZoom = 10.0 } - } - - @Test - fun testMaxZoom() { - mapboxMap.setMaxZoomPreference(10.0) - verify { transform.maxZoom = 10.0 } - } - - @Test - fun testFpsListener() { - val fpsChangedListener = mockk() - mapboxMap.onFpsChangedListener = fpsChangedListener - assertEquals("Listener should match", fpsChangedListener, mapboxMap.onFpsChangedListener) - } - - @Test - fun testTilePrefetch() { - mapboxMap.prefetchesTiles = true - verify { nativeMapView.prefetchTiles = true } - } - - @Test - fun testGetPrefetchZoomDelta() { - every { nativeMapView.prefetchZoomDelta } answers { 3 } - assertEquals(3, mapboxMap.prefetchZoomDelta) - } - - @Test - fun testSetPrefetchZoomDelta() { - mapboxMap.prefetchZoomDelta = 2 - verify { nativeMapView.prefetchZoomDelta = 2 } - } - - @Test - fun testCameraForLatLngBounds() { - val bounds = LatLngBounds.Builder().include(LatLng()).include(LatLng(1.0, 1.0)).build() - mapboxMap.setLatLngBoundsForCameraTarget(bounds) - verify { nativeMapView.setLatLngBounds(bounds) } - } - - @Test(expected = IllegalArgumentException::class) - fun testAnimateCameraChecksDurationPositive() { - mapboxMap.animateCamera(CameraUpdateFactory.newLatLng(LatLng(30.0, 30.0)), 0, null) - } - - @Test(expected = IllegalArgumentException::class) - fun testEaseCameraChecksDurationPositive() { - mapboxMap.easeCamera(CameraUpdateFactory.newLatLng(LatLng(30.0, 30.0)), 0, null) - } - - @Test - fun testGetNativeMapPtr() { - assertEquals(5, mapboxMap.nativeMapPtr) - } - - @Test - fun testNativeMapIsNotCalledOnStateSave() { - clearMocks(nativeMapView) - mapboxMap.onSaveInstanceState(mockk(relaxed = true)) - verify { nativeMapView wasNot Called } - } - - @Test - fun testCameraChangeDispatcherCleared() { - mapboxMap.onDestroy() - verify { cameraChangeDispatcher.onDestroy() } - } - - @Test - fun testStyleClearedOnDestroy() { - val style = mockk - - - - - - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml deleted file mode 100644 index d36c741c5..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/actions.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - Concurrent Open InfoWindows - Deselect Markers On Tap - Reset - Toggle icon overlap - Filter layer - Change location - Accelerate/Decelerate interpolator - Bounce interpolator - Anticipate/Overshoot interpolator - PathInterpolator interpolator - Toggle Symbol Layer Visibility - Move - Ease - Animate - Open dialog - Download region - List regions - Remove polylines - Change visibility - Change alpha - Change points - Change color - Change holes - Change width - "Click the map to calculate the distance" - Move the map by x/y pixels - Open navigation drawer - Close navigation drawer - X: %1$d - Y: %1$d - Animate to new position - Chelsea - Stamford Bridge - Arsenal - Emirates Stadium - Zoom: %.2f - ViewCache size %d - Latitude - -180 - Longitude - Zoom - 18 - Bearing - 0 - Tilt - No Results - Change intensity - Change Anchor - Amount of markers - Update layer (invalidate) - Red - Green - Blue - Add an exponential zoom function - Add an interval zoom function - Add a categorical source function - Add an exponential source function - Add an identity source function - Add an interval source function - Add a composite, categorical function - Add a composite, exponential function - Add a composite, interval function - Bangalore - List all layers in the style - List all sources in the style - Color the water - Set background opacity - Set road symbol placement to Point - Set layer visibility to false - Add a parks layer - Add a dynamic GeoJSON source - Remove buildings layer - Add a terrain layer - Add a satellite layer - Change the water color on zoom - Custom tiles - Apply filtered fill - Apply filtered line - Apply numeric fill filter - Bring water to front - Toggle text size - Toggle text field contents - Toggle text font - Zoom in - Zoom out - Zoom by 2 - Zoom to point - Zoom to 4 - Restrict almost worldview - Restrict across IDL - Reset bounds - \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml deleted file mode 100644 index ad9f88a46..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/arrays.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - 10 - 100 - 500 - 1000 - 10000 - - - foo - monospace - bar - - \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml deleted file mode 100644 index 94c805f35..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/categories.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - category - _Basic - Annotation - Camera - Custom Layer - Fragment - Image Generator - Info Window - Map Layout - Offline - Styling - Features - Storage - Texture View - Location - _Integration - Telemetry - Turf - \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml deleted file mode 100644 index 88524d44e..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/colors.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - #52A1D8 - #3887BE - #E55E5E - #F9F9F9 - #56B881 - - #D50000 - #2962FF - #1B5E20 - - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml deleted file mode 100644 index 7f018a43f..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - Overlay a custom native layer on the map - Learn how to create a custom InfoWindow - CameraPosition capabilities - Showcase MapFragment - Showcase SupportMapFragment - Showcase using a Map Fragment with a fragment backstack - Activity with multiple maps on screen - Add marker to map on long press - Different types of zoom methods - Configure a max and min zoomlevel - Learn how to handle the InfoWindow - Add Markers In Bulk to a Map - Showcase the different animation types - Center the camera around a bounds - Update position and icon - Map Padding example - Debug Mode - Offline Map example - Download an offline region. - Update metadata example - Delete region example - Change resources cache path example - Animate the position change of a symbol layer - Add a polyline to a map - Add a polygon to a map - Scroll with pixels in x,y direction - Example to make a snapshot of the map - 2 maps in a view hierarchy - Learn how to create a dynamic custom InfoWindow - Use SupportMapFragments in a ViewPager - Adopt the map style on the fly - Show a gradient line layer from a geojson source - Use functions to change the map appearance - Manipulate symbols at runtime - Use a custom sprite in a Symbol Layer - Use GeoJson sources and dynamic layers to cluster information - Use realtime GeoJSON data streams to move a symbol on your map - Shows how to print a map - Query rendered feature properties on click - Count all rendered features in box - Count all rendered symbols in box - Highlight buildings in box - Query source for features - Shows a simple map - Logs map change events to Logcat - Changes visibility of map and view parent - Change Symbol icon when zoom levels changes - Use a local file as the style - Display a map inside a dialog fragment - Show bus stops and route in Singapore - Transform urls on the fly - Control the cache management with FileSource API - Limit viewport to Iceland - Shows how to add 3D extruded shapes - Shows how to show 3D extruded buildings - Shows how to animate georeferenced images - Show 2 MapView on screen with a bottom sheet - Show a static bitmap taken with the MapSnapshotter - Show how to reuse a MapSnapshotter instance - Show how to add a marker to a Snapshot - Show how to load a local style with a Snapshot - Use Android SDK Animators to animate camera position changes - Use Android SDK Views as symbols - Use TextureView to render the map - Resize a map rendered on a TextureView - Animate a map rendered on a TextureView - Enable a transparent surface on TextureView - Blend an overlay on a map - Example Custom Geometry Source - Load a map without providing a style URI or JSON - Suzhou using Droid Sans for Chinese glyphs - Example raster-dem source and hillshade layer - Use HeatmapLayer to visualise earthquakes - Manipulate gestures detector\'s settings - Merge external offline database - Click to add a marker, long-click to drag - Change map\'s style while location is displayed - Showcases location render and tracking modes - Uses LocationComponent in a Fragment - Force location updates and don\'t rely on the engine - Use LocationComponentActivationOptions to set options - Show multiple TextureView MapViews as recyclerView items - Show multiple GLSurfaceView MapViews as recyclerView items - Show a MapView inside a viewpager inside a recyclerView - Show the use PerformanceEvent for performance measurements - Use TurfTransformation#circle() to show a Cirlce expressed in physical units - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml deleted file mode 100644 index 9d3418343..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/dimens.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - 16dp - 10dp - 96dp - 256dp - 32dp - 0dp - 240dp - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml deleted file mode 100644 index 6701032d9..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/ids.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml deleted file mode 100644 index 45048f8b0..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - Mapbox Android SDK TestApp - fps30 - fps60 - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml deleted file mode 100644 index 8d7da603f..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/styles.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml deleted file mode 100644 index 90baaed28..000000000 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ /dev/null @@ -1,89 +0,0 @@ - - - Support Map Fragment - Map Fragment - Backstack Map Fragment - Multiple Maps on Screen - Add Markers In Bulk - Animated SymbolLayer - Dynamic Marker - Polyline - Polygon - Press Map For Marker - Zoom function with SymbolLayer - Standard InfoWindow - Custom InfoWindow - Custom Dynamic InfoWindow - Animation Types - Zoom Methods - LatLngBounds Method - CameraPosition Method - Scroll By Method - Double Map Activity - Snapshot Activity - Custom Layer - Map Padding - Debug Mode - Offline Map - Update metadata Map - Delete region - Change resources cache path - Min/Max Zoom - ViewPager - Runtime Style - Gradient line layer - Data Driven Style - Circle layer - Local Style file - GeoJson Clustering - Add live realtime data - Print a map - Query feature properties - Count features in box - Count symbols in box - Highlight features in box - Query source features - Symbols - Add Custom Sprite - Simple Map - Map Change Events - Visibility Map - Dialog with map - Url transform - Cache management - Restrict camera to a bounds - Fill extrusions - Building layer - Animated Image Source - Bottom sheet - Map Snapshotter - Map Snapshotter Reuse - Map Snapshot with marker - Map Snapshot with local style - Animator animation - SymbolGenerator - TextureView debug - TextureView resize - TextureView animation - TextureView transparent background - Map overlay - Grid Source - No Style URI/JSON - Local CJK glyph generation - Hillshade - Heatmap layer - Gestures detector - Offline DB merge - Draggable marker - Simple Location Activity - Location Modes Activity - Location Fragment - Manual Location updates - Build Location Activation - RecyclerView TextureView - RecyclerView GLSurfaceView - Nested ViewPager - Performance Measurement - Physical Unit Circle - Download region - \ No newline at end of file diff --git a/platform/android/TESTS.md b/platform/android/TESTS.md deleted file mode 100644 index f0bdeb2dc..000000000 --- a/platform/android/TESTS.md +++ /dev/null @@ -1,117 +0,0 @@ -# Mapbox GL Test App - -## Testing - -### Running Espresso tests on a device - -This test project comes with all the required Android Testing Support Library dependencies -in the Gradle file. Tests are under the `app/src/androidTest` folder. - -Note that before running your tests, you might want to turn off animations on your test device. -It's a known issue that leaving system animations turned on in a test device -(window animation scale, transition animation scale, animator duration scale) -might cause unexpected results, or may lead tests to fail. - -To create a new run configuration: -* Click on Run -> Edit Configurations... -* Click on the plus sign and then on "Android Tests" -* Give a name to the configuration, e.g. `TestAppTests` -* Choose the `MapboxGLAndroidSDKTestApp` module -* Choose `android.support.test.runner.AndroidJUnitRunner` as the instrumentation runner -* Click OK to save the new configuration - -You can now run this configuration from the main toolbar dropdown menu. - -### Running Espresso tests on Firebase within AS - -Tests are under the `app/src/androidTest` folder. - -1) _Sign In_ with your Google account. - -![Firebase Tests AS 1][1] - -2) Or _Add Account_ if you want to sign in with a different account. - -![Firebase Tests AS 2][2] - -3) Select the test configuration you want to run and click _Play_ button. - -4) Click on _Cloud Testing_ tab. You need to create a project in Firebase and define a _Matrix configuration_ previously (see -[Firebase Test Lab for Android guide](https://firebase.google.com/docs/test-lab/overview)). Select your template (e.g. _Nexus 5 (3)_). - -5) Select your _Cloud project_ (e.g. _android-gl-native (android-gl-native)_). - -![Firebase Tests AS 3-5][3] - -6) Click on _OK_ button to run the tests. - -![Firebase Tests AS 6][4] - -7) When tests finish the results will appear within AS. - -![Firebase Tests AS 7][5] - -### Running Espresso tests on AWS Device Farm - -On a terminal, within `mapbox-gl-native/android/java`, -run the tests (`cC` stands for `connectedCheck`): - -``` -$ ./gradlew -Pmapbox.abis=all cC -p MapboxGLAndroidSDKTestApp -``` - -Then: -* Go to your AWS Console and choose Device Farm. -* Create a new project, e.g. `MapboxGLAndroidSDKTestApp` -* On step 1, upload the APK in `mapbox-gl-native/android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-unaligned.apk` -* On step 2, choose Instrumentation, test filter is `com.mapbox.mapboxgl.testapp.MainActivityTest` and upload the APK in `mapbox-gl-native/android/java/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest-unaligned.apk` -* On step 3, choose a device pool. E.g. Top Devices -* On step 4, customize your device state (if needed) -* Finally, confirm the configuration and run the tests. - -On Step 2, you can also separate by commas different classes: `com.mapbox.mapboxgl.testapp.MainActivityTest,com.mapbox.mapboxgl.testapp.MainActivityScreenTest` - -If you have no tests for your app, or want to test some random user behavior, -you can just choose "Built-in: Fuzz" in step 2. - -### Running pure JUnit tests - -These tests run on a local JVM on your development machine and they are extremely fast to run -(as compared to Espresso). These tests are located under `src/test/java`. To run them you switch -to the Unit Tests build variant, then right click the corresponding test class or method -and select "Run ...". - -You can also have a run configuration: -* Click on Run -> Edit Configurations... -* Click on "Junit Tests" -* Give a name to the configuration, e.g. `JUnit tests` -* As "Test Kind", choose "All in directory" -* As folder, choose the following folder: `mapbox-gl-native/android/java/MapboxGLAndroidSDKTestApp/src/test/java` -* Click OK to save the new configuration - -You can also run the tests from the command line with: - -``` -$ ./gradlew -Pmapbox.abis=none test -p MapboxGLAndroidSDKTestApp -``` - -### Running the UI/Application Exerciser Monkey - -Similar to the "Built-in: Fuzz" test mentioned above, Android provides -[Monkey](http://developer.android.com/tools/help/monkey.html), -"a program that runs on your emulator or device and generates pseudo-random streams of user events -such as clicks, touches, or gestures, as well as a number of system-level events." - -To exercise Monkey on the test app, install the package on the device (e.g. via Android Studio) -and then: - -``` -$ adb shell monkey -p com.mapbox.mapboxgl.testapp -v 500 -``` - - -[1]: ./art/tests/FirebaseTestsAS_1.png -[2]: ./art/tests/FirebaseTestsAS_2.png -[3]: ./art/tests/FirebaseTestsAS_3-5.png -[4]: ./art/tests/FirebaseTestsAS_6.png -[5]: ./art/tests/FirebaseTestsAS_7.png diff --git a/platform/android/art/tests/FirebaseTestsAS_1.png b/platform/android/art/tests/FirebaseTestsAS_1.png deleted file mode 100644 index 5bdd9907fb3e920da7564fa2b4fc3e5c1d27ae1a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53000 zcmaI81CS+4&;~kl#gKyXr$qRK!(V0nMfJ0JmnkGSX6hJPDiXJrXtpz0~y)4yM! z93-`zfq-C8{`mj{Wn^Ij0SN+0i3+KD0AKh(7@~4KJx{pREbBk(JC1Dyf=%YUv0v0-wI%MoOLJp9TkqM#b0)mk}8HQ0+Y<}BrcwKMt-}6m+jGS+#KUNbR zc1H;>VM|?~o|ZDt%_o9yx7ZtKYiFoe40()@^!_V16@>o#FEt4Wxlpk{|4Q#SK7dr} z>l$iOX5zm~bVvx}C8x3lVpCI7$4%?EwK4hq@PYqIz_$@7G&FR3dpj*mTvnD179>$; zU=SKQf1HH>zXF7~gNEzPvV-)GVB!L-ny~F{Y*;a2g9?w!JwX2xpa2*Q`Y0@xVOVHL z)pAu`Zuo}550d{G&3|M1pOIY>5~4zjd3J3@UaBdqzK6unM3BJ(Ppqp z?du$6_?#2mVj}nkXP4#5fakbBaWU6^n<5hbVl$w}o<*6mIyfz#!nW=hInBR^#%A zXR7r}$;WT&HaiHEoFxEowm3yn1Q`0Rckd1&CTxzn*-;QmVCmk!s=ovg0_~SN3`L;n z7PB4b&DCgKdVki#k`zn3mlrCRoW*anmyh)XdU#=pCJt-5(*RAsqkL zs~lokRTUf*6tGWS?}=cDl7RsM64LJRvHR17*2||HXamdq)cJ7@)`kyif52(@)Yyjg zWDbAK;wwf3&k(f^_yKT|Y!C6g2*PL*cTsrIlg;ljdx0ad?!U>Mv zj~`WivzuC;I@T;6eso@6bZUlgiuPdvrZ3U^m;KQdy6x>*r8xquAJw81nMT$%oxZo) zT0bn?z4=tdz1U z^J}VwO3$ykQo)T4NF6>Uz?I9O`{AlQ_>bk&xByU&o3xB-&i4Bw4vjrGfleUkGG zQOi2{rH!wy2eUXAXGl9G;AmE&FGRwlTfY0iJkbSE|lXrfmv?*PbL%gnT+&N8UJ z5bgd3Fc0^Fz!d_};1n`^FM&%-E&$$oCoXqW$=*TiPs}Xsqxw)kY$4K_LwqgeAyI&Ch**DvWz_mO6w=Ux; zbPjv5SW3I0h2<)xfvOIkhl9URsHmu-f@L!@Oun^BbKma{1j)zHV@r+}?ph)Y^k3t0 zS5gkxOD!2*O)pcYJD-20!r7eHT|U=(LSsdyNFu5Ge4Xbiq^KB;&+ z{3@7#bU6hUGlev1nBQ3YK0G46Ke#vWnBE?Ct*O3?G~bH$-tD>#_zX$%*h-GWJ~=fk zZScxL5SL5Ho>_DS&G^1T z^g9DtzCTOonOv<~`{DC_?=mq>Jw^8Gsa`a%7iCYz!YF`8=cJ5vW+uNL-<#KX#GcA^ zCg&zo9~Ay)Kq^Uk%Xen*_$zIGv&`BlV#6bkI3PQ#b8{><5%ANt*)!3&bEfYVFq|&h z0pV5Q2H5g~kUI?PVoR@CKbeT&8yANpQdJe4cdfw%o-B6lkkTVcW+zvzoyQs4_4-sc zdK3O<80-UqCVKi@3hV2gmMXQPfS^P69+wTayM5m84#$(F$qEo9 z*fTf|uyai7t$s35qR)tg>;tO2prkK)&gA&+yB($fgPmUTg)21$eX;y<4I~)HcR($O zlBQ1xKD3YnZZ82&h}M7aR@^`(F=~Mb5>95V!apSHlP1AMqGIhwcf)$!ssAfo2IZH^qC&;xS@6=` z!Tx^}^fdz+ZkQLMlmQ0P>MjDl)RuH9ZUB({<$_Cjb_ce%B}N-8IPaoPTx!GNpRw9U z$b>s#0Ea2sH-zgk3R2nVa2=|78eR|97uBfx1$x3c2pzTW^o8~#vUC*x&5)kZYX5Wog z&is2<9OtuSiThnldXq;8@~}FnUS|UgiXMY{z}86@UXCIbv*IubYW)Udd?n8yP2iyzU$9L9Rbq zQtahgpM#1E-{F;jJg2J>zTX?Dwv=Udd8WMYpOS0cg?N*akM53Qr`~C`xwp>8^0N&t zM#YVgP*OGp_~xo&37{L#G_B&OPvwqHN6s@52=h_!J$;p=3Dx3g!n(hVA@Z=8cDIzA zl;!g)%efjn-Aw`&Hk(0S8&Z0BxIoVnDR!$5VOT_xj8)%=CRN_&XmaxpgHYpie=g}JbevYfK|1!#j(;&T%)$MQ z%1}xsi{$-We9by5KZhy&zU@y6YbL{=duyLW2);kxW_tMfxL}c4;Pj(stYzRUH#%{& zMf*|wa!Y$2HZeMot{U)tY?+BKP6rap4Px}gCpFuv@wQ)=nn8N|R*w22g~aSHY}1kM zb6OF`=3HJ!{Ja+*ms<#6gY;)zUW5F+*?{4|(4)gF1R8ukLklH~D#LQ(^aUt$K2m3W zmD^Vb81}ZdmE;O+MJ)!vtt8VOt_{qi;6b8ju`@GQ1z9&7P760z8f4}(Rx>>zt-k{& zqCI|3H!NWnGv|Yum0K@xzQ65f&e!!Q6>jJ#2qZgP-^-LMO~G<|vcjbA)fs)l&$M)P z5IY?U+QJ53SSONN0*hUbVr8SXK1WWLIWz7vE+uzZ&wE-iwp(vKkXy8rk!m;WQcBux zeHbSBN95fGOIMkncdB+-x_&}1EpcBO41A#cZyW~$Sc)mzi9SrzAe_LS-9Y+O?BPr^ z>cxyQfrs}H;aYx?aZ%l^R`oTz*7X2=42LB-t1zSzEN1++R6^tS@+!c9K$|v= z$bgSF+o?xT<+Utuu`la*cA1P(y^YccpPCall5%3*fHM7Y zVLow#msHGaOQ#DWXf4)t{>b|~7vXE^ZsQ#R6gui~uEoaZasMKmN}&pq$P&WFy_Y%5 zxSuFrjByQ<2>=g9Il=F+XWZqkJgiqpQmnbW2=gOg0EyRC?0D3%A&c@hPpDdlGHAYmVPmtgw%R7IpHXmO9juk&o2da(UGcuL)1@a+M!usJ^9gTsLLvNyl% zmGh=y)*o!yT~oP%dB2-m2;b9P;`vZGz&DiSQIdzk;r4miLE`(&+f5AZdaU5SXm~%I zkHY;))1L%d2(x$y4VFHqANRRhlH4t_MR2NYy}B|1PFx_bmJM+bY|SX6qM?>;dTOA! zN)7_)RrM+rJy#zCl~eMmbVcs{*1h&^Qx>X3zcL`A=9>W4uE?6<(?V}_yRYz0wfRHz zAJcP6`o2bL-BTz&#a5w0-~+F%zS{h4vx11(Zli0sTOGTMCyGa{J)8XbWxmKrBvys( zI@j$eD+#BCJ{ddnxVJhq`?K7N#aIN+7_px~>2x{c72;pC3hafL$FZ{F3I}qRe{@n= z?w|h{c{pGdc?yN9Ka3Co!=4qf|_UtKm>RE={Ft5__SNjlf zAamTH#7c#IJ2C7Gwl$p{QFL4B`a?CV@wA~pt> zHZs^jk3UgkbrJJ;67abqsT>uVG*GXv`ICd04I?;Y#3U_KAucr)HJ(I?QsO6-FKxd} zXXw;mZ5B;aUEya2H2sp{{(caiZXFkmK$;jDG7kUK6<#j7@Fv)kC1?24TRS{R=v6Bt zW-v5^{c<;_Y`=QYl(6a?m4Yc840yq<7L5CWft8X1o9=ku2ReNkC+vw&LyDWe*B3My zC1HdS6~i{iERK3cdq(clLX^Rqr#S1H)u{HH?CL#DB_wg?0r_yC9#0)WyN|Wnhw_co-rx@|L7aI{p?X28^GNgIh2LEGqZB}SI*-R zQ-$7U!6de+^X+vpv~tB?DH2`Z@3ioEtFJaAgk}pK=n}G)&GH7-v+!zC8sLVnf_}y6 zciLTv)NMTBev$SFp>PUUWKFB{sf@#x(nSmJ`0RCe84_FC&aupzYY9QF9#h+2Ye+WzCcDbgbnJ53pPXOXd4a-2zE@& z5@jTOq1sI|*o$Y4JV?UDMTZJXX-jT(6<$fUP#OR&JEbRA>liWX8F#B1Dtv#h$FwO7 zz9#jFs+DA~*t!DbSHQnUWM^5}pT7Vgl2}d;5=y(42jWW<@Yje`@x?hT@L^jpAQr-Y zP1}1Me}Idhc+`U5N@Z}BI*ps#|7Gk*hChFNEnwPn6^WfAys1m-owwz3*zv~LEL&oP zy$G?=s`iRR&oOIgy0|`BKvKjw2(Re7W|Qvo(sIN`+sJ*LQPbzd%4ErE@#ST?6sgR; zOhgk9{GN(B8Cw+`=0n~kn-E%rAO7VPCC5}WZdTYuC7MJYGMc}8|npLTMEq>cz=b(&m0R=YfBzSj?6bmozU=ak?Gcz3-4 zD3v_*yuWEw*f`v}8*ZiTbq6=l6wE*|RRZ|~o4|L`B!g*8UEuo1P_1+YU6S>F!z;kB z0x006pC)ICgyKiL1Ex@2W>R*Z01C_c+Om|7Csi;H5Ngm+5`i0oH_)ZpVTtaH;yI$%T@ zZMvek9fo>o+$C<@tlFu|+gaA2p@R1Iesie1$w4Lah;8-|zHVMSk9__>R#S~!Vn$A0 zo>!aGF#^?!b_%5^Db|iN1x1`;NGA{A3VlnkBAjJyfWr{o76f%5K7w=tG}xT!Ew8+% zZh$JNuG%yWOOER=n|;m}5qqxW;|PEnBF#lZvjqXKV+L5tu9156h{$5*7?3kcn$7_Vz7~qu|Zns zT7ey-N_JAsHuUfBWB#7g1)`E2z_FYqDy|4BB~G*TxBCPC@d>_;|9I$I)GA0u*lJX^ z$=H}c0|!~o{V=6Q@lv5!Vx$#q*{|{Q`EogJ%uiPAAk5|rrsq1C984aC<@r^Yv&d)l z8h5YzHrPgg0E(qF%!cpz%JBXig}x6UT z&iAd`Q9Wy>JwmJoJ%K}$!7!YJC=WgQ_Hn@_W`O z(;5O%iHU?oaqVyk($*hBb6O}{eBMz6PuDaYNu^e`SRy`oaX;5J3?O}X9(%~ChXSh; zKx!(f3zjy-L(@oI|BR^~{W6~7bzI7ml8W}{0wsI@Of#s$J6A}S*Kq9;$B!lMGh$1c z=+z(v$ONabqn(lnwVgDm zBO^4P9+`N`iQmaVN5UOnjzX$a28tNc;UI}-~Ag*J%_odCj$xHQb9`v52El5hDnV>6M- zJv`eZiNZ@n5LLAlCo2gV!vxIoa2`9naFdF|tu3#L(B(uz&5eON(-Zw@pP2PI6-=>8 zgLJ0&C>w?h*&l6rQmGh&*uVNwrg^XY;Hpo`3u%2I#djyb3;0ihB>dL{Nk|GGJ-c-z zc)VG_PC4W7Ad2&W*hD0}Dl#e3?f8GO!kKdq!tul$$v{q96+G7-V(e;FPb5f9UY9&`*xA)8(t*y)|y`wRY(Tn?9|(dGPP{ z!eG{(`R`Zrl@z-8SwzWQU4oi`E0cCAl=Jo|f0vcH-->C2Ltd(vd)-XYNVK2^V1YM< z`-jX=C1-Y~OhdU{@V6YRMB$>u_2;{=V_}%0{xwcRYwC z_|E#AYcy?ZHy+9Sp3MFj96J>fG?SK8D)dehEVhSjbKd`g(MK`yS+m3Qv!C1oo9;=B z((}0OA~5?nwBIL5W`?5W_aP(BN}?1@k$DB19h206a6E6?{N4!adRRWy@7h3=dD&L> zm>@FL25&pMx35it2vvMs1aa2`QK({OKv< zzT~6Z>2+BQvW*e%XqYvBP6pXWz@RAoEm3t~gRih{^yN87;|J8aI zwm)r7_Jaeq@9OFpwqy8QxiMbdNTFhSsyMN0FY)}F!LN=REeQBUKr>` zu0K4RwvLS--}1!ZdA(NH+C=);0x&Sq+I!*fs^MzWBf?AlpvBXZrw;D&YFbD-3I z-Ta%0ijH$d|9*HCa$3k0ikbrY{UgVffMo&iF+|sCt=@2jf%|=PY~x1knwQ=5`mSYb zh2>!2bhfr@etNfBDbZV_#z*9}7ez$R6TVU#qFFvE9g@V#UVyn+_%N>n47xbyM51}O z-;_`|xAT;Q)&30A`WySOg1s;CtsYsX3TsuFvcaX|>$6S)7FnfUWLX>y=5t2u!iyE% zKSCHm6^BG_opdO-c>9s*)b^MgnOl_^c1lO&@49tX&Rqw;gewfw+L0}m07&w#WD?jS zfvh_IvzsSFjK%p1%-06K@BXEyZXj1efF{~!VUv9@AMe;F(zieM)muuyA0|;DI2{7z zOZ+dzBP%GF%=xblC*>jk6m7lr4mF|S@GczQOrrh`GI6tHf!CwSX;%;%(~>*~cA|yp zvcd*w=G|wvGUV{q054|H8`RKJJEqVpcAOQ3%%A8&fGKaf)?48J>`4y*@Q~MNzs8NYDysRiped96!$<8DfU+3xe_p(Bv z;C3S!!f;Rs%Mp4CTG(gp*`LJcN|ukRYh`PMtd;K+}nS7SC|-1O7|GO%u`JvM>@ z2Ur>HH)V8(eOUVn{@@USOZpJd_Ub1Z&1h>53)9BOHa3m91qbiE!dvK+^Wq)|%MzVk zKujy<8zGct2BZ`$Jih;ZI97Uaw%ep^34QWlw&6-xA(U}$`){A{i1F6vQ=~0pVD^iADUPKYUuO)ow6>RYMXvA*OeO)7>m|1D zPTu!uG!w6OZNm{9X&B`JbOGcr5sw`vQcHd}Z|kK|hqJ)|kCl&NjjgY{OB#WIqV#mj zm2UdHBgK6cNxv4(c*lwV^gPEG_;nOGuL?NIjEv!Gp4^jUrB>Co@|L?%F!3j0M+|?Ha$xR(b6Y z*^Jz%nO(v3B&@{yYmV}r)-d%6aj?OUD<8}9mrI6nC!x&r@8|T}{umvcWn2ZXlMuvm zL>%|+(Jq3@b%G9K*kM)=Ra}&ZxZJ>u1bF;BB8LTr;jMAjVEFrN7kkY|ubQ5~VEK&C zxe3hC0V)IH1YUGfQBWnkeAh1{70!Fxk_dTby5X(M zV)9-`Qn5boJ)hV5v{zeXk!l9^{vcfbboH|Z+%Zf>+XnN(OrJ>=b?NY;g?Td1O=Jxv zF?CN21yw7c3ax61RZEzbL%x-+(MF$dg}p_@n)xIybX}KNYG}WQx+Rv_#a%L+rRXW& z((;z5h7M+~gKzH*#*}-yu0&icXe^)hWEa6%TZMv8ImykeeHlHmbucuJi_c_7VT%?> zxRd0(UJU*tlkkDPka}TYs(g}yD{BVivk>*#Xl=>?OW-X)KHkJB@c~}*PX+1?D$7tf zv5?x%b=BBO^1BkPC78W#BJ7N|h0R~G=f47D?rk%41kZ@!Vx8pWoF{LRJai@#Vh{dH z-@_q}PEGk9#7k6~cDh}z%@#_qKw*MGp!9n{{!OtODpR?F_F+OFv$zEJ(#~N1`U0To z8c~IY@Lsy8<#x3)--fu)bp>Nb%hM)5i8}mfn+$L+)NR;(L) zvy_S_eNUTcM%J``cUJ4(t&4%nf+Qi@^EtYOTt$$K&ik4VgTZeEtqLeFY+e!?$mO2D zt|E~Nc%?RCGPuOFS(yg5?N?aY*mN~&Xz9pXL2tFrxWPMYxhUNOAvx(%=Mkfh%sA3t z82UKkoTpu=1uq=lCumMApp)4s69K%Amc0t}XQF|0Z2Eef-o7KwndtnKgBq_q>1z~W> zFaRq`zlF-j+>|7stla!?!1mv)pJJsbI2=}Eb_3ssRNlxt0?Y zF%#z?jLB0dpZs|$(wgUD8pq=U4k7QI7BEf*uwHZ|V@GIVL&=zGUT~pift6)$j*f%^ z-d78i)Bi9;@Wzt3sxq{N$_U-Vwn?Z-yhX<^qje^O0E zfqmWdevkv5v}<=Twp9S8;6C6tYgRMEm(CJ&l3lU+j!v+!@eb{S>K5%oSFSsjd|5Ov z#?o4LYGLU4nf`;fK|oQtFVQ$0zOUu%i6qy%|L)Npq+0vul#$4PIUj$C9fS~|nw=LB zH4X#KzX}BKg#W#1BQ+@0&x_*! z8k+ylHeeeH00;mgV6p4J7RAJPI#{W;Z|5Ulm+NV9+C%8Hp_lJzX_DC(c{dnsk zF4YUE&w24H9_`FpI**S%{P#`P#`H39M|>4bRHa77euf~vh@C(9rM}{czGHyx!jJuF z_@|QUZyi4PPJ$OwQ+ zAEOH}zyW@Vl9F_&?a8faNJKf;0}5vilQ4 zi;4H!0)NI%RekwuR(dk^d0E2W&Qd%3YI!Mw-5`KAHDGp~j(m=-s%s0eCcV7x$_&pRJ8lr311f27`jC{S~X(s$qG zftGs-?$6eEm-5je7i5Y`DncQ7cFC_RdDoS)roz;3kHJnauU9nZ=#lkN|3>4Al#vca zz*$;bDp|Cev>-Efw2^%n>f~*>0Vc3VM;F2_Zb+<&MabqPr$*)tnsMZ1f_HqmzU5{q z21k* zZ}{3L1frwk?2R3d0U;2=ga!?K;t7I)^%pTIpg+`8Q?WO|9SIYc=wsAT)pg3m3JwA1 zp+Y5CxvXR@Q0R!?{vKI$iCwL@3~gLva$Z+a+G4LV}95U!quYQ*t+!fQ13 zaz8?)9}4?nOn)RRJvka8yloMXNC6L$t6ykeO*+mX6H)J&UDZ&%%3{9Js->u?iHV6A zm&1PdUbT*UcxRce5+Y0_gz3BTG=K05MORNxTU#4}fN%I<{H!gg67!1@1v5SapdwYL z2^+wj4bLt%l|9|0r^LB`B^!$4VR&CvT_={sNx@A`%RPEO8md74HxX?n26TF&_b?x( zoh-)@oNbn8+ReNUy*Ep3$ zjx_0~SLQGP#+VFN1q?N>C0w)U+mV%=uSJ;Z29luli`fJa+dP_{LX^O5(cbY$jC6HC zYhjtBKgy(8zbEn!qxLll>Hn0tWar}UZqTDOv#|kd(b~~55Dcw~Pa;v2#p#+s7%n83 zl#tM`czEPg{cy^90S&Ji2`$&yU?}fYQgkmYhB2^AKRt0ePxv{t>7{_rV=T_;_905X zL>uvlGy)cpR;RC29^+KLCCF-=R+^dEuM~ltm=-`tL!Vt+u<5VbY|y)VzOSdFVNV_! z6eL?*zrQ&g_wM2KYp4J^Gc`p0z{b}4`r<4iHoDE>k8V5?0S9Wq+L}$qH3h9^z*ea^ z1a`q*Md`F+3A&*-@bBopQ%+r7GGU^x3st|WlA2fPB7j85s9|lh?-BJK-UYRLFj{Js zIHA{f|Jf)Pl?+n6cGs&~LzxgS`S1ukdyBtM>Sg27;h^ILVkixYC^A;HE#aCoWc|AK z9o)~yNOfCzu#!3L*^j_S~G4)uf}& zk2$M&hZO2l;B;xylS{X^%GXHmg;7*pLfp6wPXHrsh~Leag@sMC%p?*X5_#eu&GaB- z2waPB=tvnYl$n4fiBhj&z7mQ?0Bl$8L-1GWg)3`vbdP#kMc5`CR!+#`#>_S^y-jtX z9FlM~sW&mH6w9B5wlx&%;*AcHlD5>Ol+aMnpgbFe#JN(yp!nA~PFJRZZ16 zj7|!2T7B@IMuJT5?It~I@B{K*Fvl+w0AB%k>5gI5{AhSM&^HjQzL~gt!;YC;^yZkm z`jL3bNsZ*iY&>rG0M#9gr0|M9|zfIcCi zywQ`hEB<6>^@`b|7AU~5$ft?dJ- zrQ$@~5Jvw)c=eG!RFl>EA5ngbzty6)#bZ?_IKNxtW0;i&9<$y8BLxo$6DbeicLE-N z%Gj!Hk~!l=L}yJVfW5r`Z12#b=KS#Su}e-$Cu=@LVt3=^;{^p6{rrJ8@ohs?mr!z} z?l^q%kg@i**3M0iH8J$*?W16tYbIRjrF$R@A(b0CAZU13#H2V5w~PeASpewyYf?L# zx&o)3n_l~pY~efs$zhtriWT667aW(=^(S@J<&M3z9eZbgvJ#)%l6giW=t-6tD*1Hh zkJ=^312ckXqV;xrN5%*ANKw^Qh2HBTlgOAL#mFIb3q_5g>?}yyW6q>_tcM%n1aheo z$3=$|5?aohGN&vaeb(sEfnNy!1~7sC3&7&&`7UX-6pG6}A*f5rCEh_lHWxMu)U9>( zk_{OI@BPUnTx}<9-8p3qf6Fv0@Ur@2!g|USbl0sj$B=+p>Sd$SOOs*N2lV4@Nyj0Y z;Xq4Bq(!b&AMMQ9F|NX=`Ahvdb5x&kt11f*T0b`W0TbY1(&qadHaEQ24O-ixnqK4h zH;Rmjll>{}`s5YX$>&pt%U_!f%p)Bh3^Kg#o)3~16B85V27)g%l=yQw$%I0FZb=Z= zM)3yERIJA)uPsmys%kS&*g?_R^f)V<&xG3)!VL8WMjSwS9k5Z&_nt;NdwVbZ$>+Mz z+kM(p9Qk!iCMq<#SRhCamnsn+844x`{RG9*P2P&UhJlZWXtC4~It&})m!cbkCs4B7 z;6gl}ltNVNxePNT3_Ot{1@hK4AdkzRfEoeSCGWGtqVxm*^nj@Qm@Z8ivMbc0b0Flg z#paKF`mmZYac0U)O+xdUu!Tv+gVdXkKmu`mL%$Rz7pngA%A(9#jQY@2^x7#*_@;oQ z&A|Z0{y!nmH$JeYkO)@aX#A|*j991yq^z8LAZU=H!%8I1yCGfTj?>TWhhLHSVxKn!TDqLwu#x1NdpFtOg{K>!W18o-1MX>L9{q-LI1BPALr*3S=a$H5F7nVC1T*JvZD zY&~E_M?nc6q(6Q32u1*-%h$J`=S|2zyc4`HT#r?xq^u}wJWRl+al!_8HsKJ*3m!zk zWWj~1TdNHaC;v(rniVRFd!Dop4Uyzc&X~ftDjnDKJSntRu(Ija4kr^?A`Fl@NSY-| zK1R^9uT}Z_y6_h6*9}fv%d?}exndpL5corwyV0*aA{x=~e9Vf_d}D?S80arb>n1ZF2?1#QGB5exL?AST1I zIfMld60fS;?9VkrM)lXfH*A$a@ChnoG<#r8{=IB({NoR@H4^Aq6rrDhgaj8~nxZ}F zAbvklFnlB7TQQ(AN?tX^XFgV_EVPm_bx9ZGix??iZ8XUp0;X74^VB`n6?4khnzJQ3 zn1rTzb;zGs)_#TtHO8SbuL7nk0W^v`m%3MB-5nk$h0*@05-zhv5jac0Jr0dFLZXON zEI3TW4CZKxp0?fft6vO&Q9=e686_BiK(DW=sq4-D%v>Td5B{Dd>1MqDyDrl6xlC{b z_@F!f*5iis+?!N*^(KzW7e4^rsk~26mY^Gz8##_phQQX{#QcZYy;>8#k;RbXBGpQT zuvjfhf~l=u6?jbMZdtsJl7j1qOJHHx&*2r#jAh_aVgo$FCtNOe_?MGXWVgayYA z(zQ+CGY>B?f3QStG&H<>`39DaY*e)f#G`d?;6OyyukwQZcWp7^(ou{22Cw4~T)c^M zaXIAWBpLh7xP$EF7tpe^-!tMQ|Km22rY!Kqa%GuB3Ip0D#~?qoq#EMFiVx4J2lc!(R0@8b`(R&#_>qiQe&Cc;0$XP%NpV5#$+fmWopFD z_%HC|H?$n}wztfe4aoytS$H+YLP5d$`6wCJ3!#kAAxE@~@b?c-iQFA(&%GlgN$(yU z+|?;}3}GA!e+qYnB0$km(aqz$5!s|>#bL5Sy31uGC2`UoU;Olt#pjo$PhJ1v|C=L@ zS}qq>&r0OB*d{vw7N7m>;P9d()7-zR_^4l2zItS*7bx8GcD=vl3dYn0_klJyGPuGj zt(y2AtHid-Xfz~-9*zuSXZESuNkY@{j}#1eN-@33l`|YX%83_fYvLm@%a6L*!eyMi z+Wfx0;zN-0QO(2PXbfW6yePE5OYk|naK?O8cLEBljpC;9@Bx>EFxf-LPKEsd6JI-K z*sn+KGy-ATL2KD^Lgka}x08flc9S97m8<$vRo|N?!vf4aRr_ys3~qTx9C-t1cI5*J zYj^PJj`(qLQiOL3S-D(n{T=Q^zY-_kyFbqY>0fWjP-x(7H-2^}NI~J}yYJyVm%k1-W~mnTZL{_NL* zeE{#x? zc|#jkA|8(h`XngN7FY%jWTi-TInbtdD=5ew5&oU^Tl zo6c7Ghjrn@m=x0$-GEUVyaqHL*;&_N8lU!P##(z6lZjQ-2zY*o|EA~n-Y$3wHM_CSirdJ^_%D+_U;sPKpTz1648DA&5+++w-#~4E5^XpRdt>~%%C*ij;R1|d*TN>M! zH|~GDPBf4<;>TT#N+0msb}GkH=0+wI2#iMH$+{U+J{2*a2>|cSBVH@i z1Z%WwPk{esQa7n~HownpL*5>6Vq{)0t9IWkhHX%6n;Pl@F@-`d77sOCxc-*r%W&6M zW6VCg#`{|{_X_zr%3xU#j;O|8O!{^ZkRG9W#qC))ClIIFl!~v3Q+K0P3;NXa%vM$1 z!U6)A6zDRN?mturk2WgEo1FO(Tqu=?Cvr)_+FH~Jc)*jz#^DP_)gXs1W?nfP@RD{J zbBGrtR(y#J|D$;iTy<#@Lv=J$w!6);z2N-k=F1qN6$(>w;su0KF&hwDmWKIbNUd>` zm6dw}c!V3Qtii#N1&+ccRK8#mC~!SxD+&Fhh|%d5LD(YU!xo0$vVUccd_1g)e3H0A=a zNO-DomzFUuS5`=aVc@d*kIcx9K0EK#DX56OsAa=|Q0nBmTCMj~V$IcYbvyRG?a>eb z5GtWjeiJ83H*%Cm@wd9I9;ZdN5|@-+;Hpqio|d4*LK(rj#lYG}J`otaBHU5W>^q*8Pu^w4pO~U-wA4S2 z*k$bBwb*Kg|T;vIZFfC5En zmIn}!1q8syNcU-#5AHyf+S@L}nvNKaEur@;t@f~30k~5^jyqYcqD1T+J5Om2$}Oo*W#92e8Ci#MCk@>#)yrZ`CeSUd6550{Z5&T+lq<{G6$*HOBs z(mC97vY18szU?d$sQ$FL+NWprCbAtW%(veEfJNV2$VT_ZzZ8cZT2tpSunm}?S;x|E zS*+b*xOr>NN`qct)huZE=vOywFzMu!2|Y@$u?3e9sXEmOfOn<^wx8eQv(2f=#cnwCJ+xI zpXnuB4Ywq3jvpf9yC)Hr3xppk6ou_2u`L#?o>-ay5}>Lsu(pT^m*nALr+|rHNW*z& zjV%>GG%KTlmbulm1{YTlk=7ZRdViy6R8CHsk&WwvOWPB>^3@*o?ak_0Bw`|Zcf|21 zvnuUuV0rKLy%RUd(jpzCMuH~sAQxPte8zY<7`Om6tg_k~4XL!!W7c!vocR7*}6GOV!=VE=Z}}!iedFKIhWsN;5(1whf$AcC$mo zNI(sHBbWpj)Q!qJdoGnIT|;+0=HDvK9TVC}seDNz2VGrB5yv`@(YUhU6=#S$qT(5^ zmw|iZ3cF^}5(JR}I{oz^y4AWNGIL>w*~pbo+MzX<(oEhVItCmshf zTaJ=6p{ed}B4n^Viyxf0q_mywQurA9l`Rdj#TSFgFE!%9+1ua_4HH7u8F(@;};@tEiiPgoLC!($-f z8g#H^qQ1yw3j7o$t?wOM|Gs>JIm2pT$+xI>r^71K6Iya*$d7uUV_XnS;t92KP<2;0xW5Q}FG6PgUHPM(e`)qq{+D5PTc!p= z=UcVGLz0=t_-7WY+*wZH;-&C!s8tdnIonG={#OQ(*|{|<&x(^2f|Y2nw1<)$o+0Vo za8z8m$w`UW+`Ly2$%++}3I_0OJUmLr5?0hEu@b<_9s|!^w$KqcI<5%dFE#eb`tvK1 zOihlA?1!2JyEeu0iP}hwokC>B(J?-tl-_srD_9=knv)}BJ3JgJ=5O`L73KxH#)^Mq zMCFucc71|i9)8jaGX{4`uaHRJRV52WFSaZpd7oQQd(c1$@DM4%`Gg#@@>a0{Oc8uu z{H&@$sy>e?{`hAl2~?|l(62BFFErex!zjFJJ}*GJRdeBK6U#(6j#u?Mt?+4GbKl7f9y z14Asnhia&#Y79oY>W!tMjUJYjH4ANE7p&Vsg(-B8M4$4~xjB^-nF}}lC4zo~O!k=vx|&XPKho%m3V1VDsb=EWK7*HafTsH7|nEaDmR1;7;74XRA{n z<)e}GxvHla52_t>S3A8$$yW?h@yO2Qp_quer~=~Fw$!L;D$cO6zweI86xj@Xo75RZ z?Jc~t9RE-$OelMJ^b1}5Hpvv~0M3eB`pTx(x>h(K`7WT-k_D-H$-mT_=rB$J0{`m_ z$3|}$Tghj(rxB)pr;!=rYaNn4n!Ymy-8)p$AXM4I@tMx!)8+i)LO^O84$l@2X7g%V zvw8~ca{yQ9K?lIt3kjG&6jyj248DP^2U;d5JcD3E#!=l<$6nnmTX=@yS^RJyJ-AE0 z+I7sI?YDXSKe&3w@XVs7YcS3o+qP}nw%M`Sv2C+s+fK(u$F^-dllPsO=X+*;pKI5( zQMIe~T2*zz0oSqpjf-~mj7g*}Y~ZDFJ}Kwv{9d=g=_m6P43R@l7(EAo$3B7{ijPn^ znOx3_iU&cwl97}Fl1w?!I4k2LdCcFR3s*iA*bH)=?uLz~O4vaIIP!E2m^{xqM@i=hxsdYFpSo;Y6>{rB)cCX~RB zk9ujoD}LiQVFab!HT$Qaa!3_7RH(!-`b<`{yN&3!d7f_WHSCYHFmgGD9@k+;)FvE? z*3W0rBQLWshkKwzd|w_An3k*GiRER&T)^`-ZnG3@Y&>{}#b*CGb5Pa>KU6eukgfH|wh8yJEV zMyS>?9>jZna>jqa1P=hNS+oVU#U+y3kvG&6_Cpid1O7{@6|4Y?l}F31^-g~d@wLxo zBJhMVr%Q4dC+0K)VTXt`Dw2SxxYNQ*-|Pwr7gw)lv&m{?zku(yPYNfoP1h6&_A@q6 zKN59?6Z!2AE-IJ6Tx-lEk~OBYGH&Plo#2=Dx`Bgg*rtM=x_Wu6qoxrcFU;X5&@leV z_e7><*P|0QGSou9yqx@Whg+t*^XFx5=lP_0#x@Tt`s@(hi>sn6ryHdI z;f=rd0lhx!H`%Bw^7t&=cs0^CHo`ca4!=ZXT~H?jRjt@AZX3~+1dU^h1m&Uepf-?D zI1OqLD_lTUF!52~8@W~P;W9Tmx`TXF8@dsTU)K^tn}LGhFRExXLI)$eNZ5)`mY|Go zB*S)Arl6btUb`ZPz)$I|+;p%ZKYWI$v2G;yfpbr4OHWMbtXHLnKGZuoJA=+6q+8E7 z9&`f=5m=FKsL(sN_{5c29TfmrI7d9F`t^@`XeV~O)Fc!k_bW#8U8q8czLig&zN=%v z7*M)D(cV;S8Zk*a8Uk(z5fm#KP+W?Mu2Rqybt>#G3i}TUncvu_br>G*aN_P?O4l`& zm|`k9J%lH3hY-Huoh3nU!SzKqJAcIfZi-aQ95m>#8zLXa8kQQYOQaYyuos%yQt)BT zAqWaREW}6`#sq>*hf$s4P>^B6v(UN-s$tK1H6MAj<7ALODzqaYqTOjwNRKBdKfj2x z5eW1xE3&3-I<%ZsvaLZL>ccQ@9JKZ^JdNk9fzymbNEGicTz|Dx9r_ZHFs8ItHrim# zvz9$J|Z}&}9Iwyzqr0mYOgy#Kexpk}@C)@zyKD#KYpa-=$II#yQ#ahDOOU{QeXuh+|{BUDXD zDZHX1We&UqKPNv6YmNo|NuqoFW9O1B#f&hHSEMz%hL&*h(9a(8CHA@{1YUw ze0CcMb@+ss8cC?6KSK81OU*y7Rz(I$X)~ld@@DVQ!7AK`$JcX#>VN6ncFNMy4o%zL zG22;cN{NqYNvb59ER3_~o#1x#L?U2lsu2Xf1q zXHZVEN4!JDG7@nqA`NT9c4$qlHKB>rE(bv-|;8{jrCq zXKyvIz~#me8$AWoTHo_P#Gy2ufnKQ3vpxU;){Z1_B@m$8_Ep{<8djrI}UKc;5} z3#&fK4wGN0fO;0i6x2o2(3m^%;r0XXeTm>BJ)x$!S+4T(b1h4AV;V79>Q`ZDyAXav z<(@LOa|52vN5>8K<4tfV5mSe=gqK^9?ZL0i$v!3WguK;c^>vP>O!B%-28AI#5~Gx; z=Dkh6V_T~(Sm%|Z{Lj$j_g<38&2VATs~rV`4CtPdVfqlrY%|23ZvCrEG;{kSgWJ0Y z(cCVMEdgT~nir6>kVKM8{iCF^mK=y`2>NQ&@xjrLgDGw$5y$6Qw(wGYi8Hn*vplJs zp!eY&V=PJu7Xx~5_QB}1@XnlonqUn4G}Om6_-(3;p`V&6H9-ruf@;6Uv{LLA?-MsV zF|FG9UhCu*QJEtCXVAVMQ0l^TqR7p~CC7t+KC#zPQJN0Gpb(AAr?xOpwe=9v)(kFh zd>8w_c_~0dKqxtl$4(M(1Xq_ksF?T^P{c*t!WCp>Y{7Y@V+p9zuJYQB(JjR)0Z@X4 zyOMxD5(+RURtgTs%GiF9*XWnsv9X(2#)y;0IcfxYHVHp{aVs74f|eqtyG|Cr^(ZGi z&|g`^5>|hx!R-3znMmrt(1D4s0d=_-6%4J31V0Zvo9J!Yzi3G8!R&WMT!Lf)M&@4T z8X_WvxQd?>hKm9sdRnQJ`UJEHV|;fV~iQGnOo$V^Q^bFOw1S%^$zl}$1y zg+Q>lc(~3N@fyv|0(wgIk#}*7VHHRKdcLbb_6GEDFdA)-$~qC?n5P3&HzU%_HN>E3 zE-6UmV-7v!-uk1-ptN_pTirzYM2PN>Z@m;QdBn(jd-R8geG=xqpzA~XEW4Kdt5HK;=O~&keUrVD4%t(gj~H_Klp}+}fDJcJQclF+ z>#I{>FXD5yAu@`A_*?(j!qem_zf?g=;CIXo1{G~L)ph6j3U+994e_uVk6=G?B1S2g z$dknJx4H0MN>Gx4(3V9tZs9RcDWnKV4d;FJ!_kCaCD3@c9gyftnk)>P<%Zqy= zK_p>2Y+_}m++J^Fuy8dzen5rOr|}(I(SqZ7O0#{6VM5RR9Zj8`I%3ANOd@W_$_8X) z>pUHb`dvX(735H}^D!1uZID68%^K;y>2N;_h1-I`nDXj$aUDbk#`pxX+L_K~-zh{z z;rYBW%(iGKh$17Se_OWa=*SonH@rSfZ8pynWVu|EtUVg!mNPDqr*tAIjKay@pHD7@_L@{(wlv0q#PDLX)yuYl2@)>tnsA5&`_^+z+xZD!`8t%h{aYL8= zVdb|zAs@JTVgnd)Ef7vPcL;upNkI-nQ_u&d*fnD_5J|SdZ?j6%chnQpA4$uU^BX5@mKo%{pyUv+<;w7$a#LpostzoRJ}*TUx7U8>-~KxE?o zWd6Gvl?Yo7k331Zw4yKi-mJg#x6oou#^RbT zjI8Mv8fLB(v#<7HDdH<`%v%aJ3waR+=g}7yGmwM{Dw~Wj`weeqm}T^rY#e^j5cLHh z+I3!%{sZeUQws9%Kt)E#mWSliC~RE9s9G-1h@UJJU8k$;fNciVR+#Jc7>gInZ%zEE<(Ve7&g5`eLQ#!mh-`;2oL%0fu`GNfgyotf7&#_50+o=&I4w)IhB`2+7j2lmeiXA#a~h&WZk2 zMMFMrHe9#bLinklktll@xXa$$0Hfg=Dgp(k#?ovs7sn9Q)lk5e=>?8JLun)gfHfgF zoD%95LF-f*Hh~Zsf`dN67&?krbJ#36x_i3!^4rc<8*>V<+Ff9_O?EXm!_|y7Mg63H zK~-1m-n82NLg#D?3m@J>(8%!5_8uzn)1qS2P~B1+js_wNxW?S$|kdhH|uF|o=4eDvvN^#?u2@9i_#A6itsAk z{#0}c<|OJ5B(MsCQRkrzv2|9~A65#l09DPe zla+~DIEZbBrk;Zx89qsT5NFLkPjfuqV%)DXOH5MsOs3KB5HW>Uc~<48@f$KHz^GOe{0RrX z&H&blI)&P;XXhZgJivV-=bkY;WK%mqQ?={!TrO6DKJ#hkGn1jGU2}Qakdi81(xX^y zUS%$M?kZ@c21@VavnW_c@&Zxpz5DV{XK>sm+k#xgfU6>3-VBlqs$j}!OpvKq-4eW> zH#Xc+YiX(ZpW6m;at87SYQp>l>V(IZ#7EL99=XQf7PyJG=or{X5s_M(j zyq%l7o0F@n`Y+&Gq11o!%s3LIIr6hbTx80SGfxGu58MyO8%PQ?YjBdiL%PTKux7!W zmZSe%7amZ-LaZ%^Y}Cp*ud5I+SMzu;9QV>Ah_4wVR6)!n48>uJc9i6Olq+Y?&ZAs8 zVpj%WlSr|$NN|VX;W;}(Q9$EZY*ynp*P>2l(IHC;U6_e~VY!1*s0#t4Hu;!5`Z)ny zE)Sfq69!3^QDu_6xNfK(qMot-Pp}o=p|R35!Md(%xnLT!zA*ZRWkEac%N#Tg-!s=Y^$#!KmI)4y5)QY>*S-Sqx zemO9=N>aU?DIl@~E+G^x`${rdn=oPDgR_N5_NEQEAp@kSNEnWCd|vJSN*g`T|8Z2E zaJC&h++Rjdk5-ht-`iyGPp0St&{dxIqcarrU?{xIp|M-o>&IZp2J!^c^^FI%| z9vZMBu$*E?$G{+>ylTNSC=-Q%k1l*7*X*ur;`L8%(_IAn^CT6KpX6B<`Ft&8N62~_ zPD3K-jzo!hQY+W(td-;l*--lGR?~btY4ERjvG2TEB7YSa(?9wwQFu4?`{PLUUU`K$ zZ^^(XoB)$3xbq9XHd=PLdV?u(_V4OU}Lh{qczjOCEhj>9`m?&w~@DJkzYp zDokV+gpHF~C#NW<1Kf6j6+A=k%bh5jaV(%WgsVG&=Hbp>UT-FvSQ{{BNhB7S;H8 zMQJj61ewm>hi~yS=~KpqfrV)in3bddc&;k0sHX}=42vDAcf_S--#-1my(IKZed6wu z+Z!j?)j$baZ=w%LN&}9UR&x>bt{5=RmFV}H)nCb4jRabmS*QYPGv$us||1gu# zO=-;kSbMT>F(@On&{X6W zxh=W=&3`vKi3M})>f6zb2#3=Z`&3p-u;Dvr`P9~ayMH7s`qlTywKh|A8liOpH6h@O zjMm;ladFfh;9X?Wbm(pte#*W{uP3q+lBoTp}^~{!x{>W#teQeIN zV+mdR&>%p)j&A_~I-{KF2|f<)GJKP@YaNtH6P6z4X`f|`Zh@#_>s4vtBLgs)d;~Du z2}L#hCiE{uDV45K$x#L{4tLQM{**vFy1PKnw+1cFm_C(fhKP0n=dBqQ>?}f6W2oY| z06yu>jN5rTpDqduPuz&l|D^qN1n_QqmaRZ|j0$LhGf+(6k`&@i(NFwL%?n} zJDK4Wjyno~5cWHD@{QYoDV+qjv|`I5RDGH84a&sMwl!R5fRd6_s8X}^6uc%(L8{4l z0K_D=c@=v9v6{WcbF4UV42&n8t!EQBRk!jNA3)bW zw=~;|3&jpnpIO?G@<)o6l3B10`S+*~<~=McIq56dbX3UiP<4Os%j16-H~~PJV*&6y ze+_(czuR9wg)7|xoz66ptgzsFss-Iq{$1H&Y7}A3@W?@7^AJTMa|4QjV1y@3R$Ou* z@~p`&&hkQgG9YK}drq93+g_86S-h*b!jgy^X%S~!qdgE#V3>84P1Cf_TKwjz;Ycie zIe-Y4_Xe09R0tmXc~zTn@UO=s!s0Y<0h`rsbV` z@m16Lo50A4RNjBWZLu;lyBBl=(IK{R1bFq9&{JwA=NU8DaOuH9Lt5WkL>v0R2CkOY z*17y}=-nsmNxeYdDy0n>0}|{F*oyKq2rap<36eQisF{r64DH%f4C&25rij`rD?1R$ z^NA7)`T0T>;END8HA1wwV>>S7%r2I@`}qC&30v8ER&9Cc6IcFt8Z*}Z+(AUws%*1y z(i(wVo}rW?a#N7slfVeEDPIClQ()aOXKUY$7LBGJt%p7azj$IB?MXR_Yyt!=S3ywM z=0Z}R9=jyI^|pI>u4kXN(Y5iDtHCHuEj5t|5fcTU7VEP)W1oGJOtqGbwbmy;rY%gg zQpvPhOh%-pyzdffuQt4{+PAT|a&o8m+`e;u=ilzOemSiuy{VYnRs>$0;TILmD;J0I zTO|M%BK`O_MN36By*eN~0EWAV6z98{7*LppBg>ro=~;)@Tzy2$NM8Be9x$2$Iv!f{mwp0XsMTKWD8X790OT!3I31(lR7{FdcU<0 z&3*ir;H5{tEXJZ9y4DwXdE64%7OJVG!o&SFy)+#5qP^2!a(laU{1u9>^gfa|HAUm%n#uV5bW)f(=*j z3}DK2t_Sm4Fwh9)<_=-y-TPmf|53?(re{Mz#cHIKVnkgVW{1 zFQ!zeNUgd>ouiCbI-c@&(7k$S-_Q0a)6>F-I@86SQ=T-k50Af>w9J>`f8X^tVOr)r|tS9^E|ntJ^y|n;*3{=*FI#?GI{U}PcG4W5Z z@zbrySFja0IQ7iarUocYfF8>n{b2ln6}q_fX*RvU4UYWO>IY}AD!a$0|`FqS;V9XFMMsnak!1^yQ0)G(e(PHd!e?P?WT|eD#bDT__Pmc7b zs@!dPj_o$^FNDvF`+;#)9bMJ2u2yeKi$X&d3^D=4g?EU>=8dPt>&e1 zh7UisThI*!3axdBE2|>ZIg=24`r|hHm6^FJ`fQVl_iJ<|$w#qKee9E!7dI;gEi4$} z3(sGDm6=&jFN&`-(4js%EYKK1MlcX)^RPj|sh*Jwv!wgMWXHeQVd^a}vMwD=BDBSL7FLxpBksb8|&9y>baeE?Sv<8e? z&E})lo>w#%-DWdB6Hccg#}}JjnWUd)hq)8SbcJRXtp;slGbEtRV z4Bah?5T3t<{a8-VK*ah0wIn_wG?*nP_ft16C$zv^Xq~CePRUWt;gi=@1)tkjpbq_9 zF`A5_!qM`!yY&@B7ct$jnJFv8XD|y4^BC4olhmp3Xf2l!dS~h0^Tk_jE)@>wduJUU z!Tmd@LHl9}`G%)_|D2nyhi9D>he9ys6lQ*P%hywA12>&&^{zfl98e_)Trhny@*5ou z*ov@^2k0AAx_q(Kmh?TwKMC_{>lc|^8D9QV!GBjf*eZ9FbPKUU4Hn(Jhg6zf;2>AC zw!Fwj9)A;|k`^xq;4F3c2yvr0m+B!8Pbe%gMLpSG8wi1Of&E5^_rDD2=||zSU5eOx z9kVsCdtRhbbt|;|3@QkgeOl;j*t+i?&eZNPbtbdHWLOAt0Y44q>FoF^?GPmmOnyE6 znhsnQE6y{arSjQ>VT_ygfEuf=XbrNmP0yA*F$0g11cI7m0Dj2?5I+-*BYfFKG$;6x z$iPqzeRS+_tfd3@$ zxh4@bEC?5%^3t*4#!Q;)`@X^XVpCVHa}s@W54F!0)<5feB8-GMU?K;I*Wpc@&0%#K zj-oCekk?cnUjy+L_&=fqL4M@wT7ns3h1Y38(L&ZI9DLWamtjo=xeW78-*eSes@Wuj z7wy0n%>v9GD(Q(3gLlCPthe6u>3u^HM-Q1zA6SAh@Z^YobRP{leSW*Z+3JJ(uO^Jq zxU;7mgTX%0Ush*mqF~sHRop|Bn2KA@2Dwxvu>BQ$ zj2x;9;Ox<&#_hu#B%|(|8R-76W&iJ7|921Q?LqP^dY^3Tw1>=^h4yCGFK&B~(545H z4IRh%bY1^U;(z??fR1hfGlB+O*#8;#|1S7PNSz>*;0D;BV?3!%rJT6)gP!i$2j?5e z@*;6dsEK3b4kI+~z4NXE>i@mt|348h$^Lchq+x*fAU>LuJUKU zgMRU3yopdNh)&J(lTK&qOsy>%cdnQ<-(-kU9HVD9%Q6wHMMLsT%NXz-qxzo z;suU#t)~^wx#4tDI9*Y@lR-aeN#A|jH*~s-S1Q1*JP&m<_Hx*2r-Na4w(-35E<)=| zU$gVhG^ny)_7dZ^kwD6}%UHr)&^vvz3iKUI)bG5{`9CZH5DdU zNH)sDLqs&yRxVNoX4xeUo)t65?bj|eu9Oz|D^~`UV**$yU*mP-QbOhDY)EoqRAMoc zx}5-*IHCqm%#Qu1A#ITf#vvNHbvpZABswSh5$mrcC!p@;&u|Cu)04Kpdx_yjKSdRy z+ii1nl^9eKURL}9>KKzu)F3D6DO)Sc_8(@2~1Sr6OqPEd?VYWtZ7L2E@=O; zcR>&$LLe!=9&ytzoNSS4jXAi!Q}wx*WLIxTXtC7I(MlW#YHLebNwCAzm~d>P5kuH~-!fCeB{2gR3zt7!mD=8>2KMKkad3l6 zAU8D#5-l7&($e=n!hY)buLJFWA1@e4vCZIjp^P#WznyVToSO<{N77{!Owu3zc}f_x zF)?TdpoJJ`f=X7rl6cNWL~!^&t1Ih^3tNiIzb5~_!pMYB#>P+nzKX7*O}3Lxz=o|- zlctqP?h^zZSeibQgIt`tPC2DKi?y*-k0#X?9~!gx#GJ2f#J?H&ELD*_J5%B{`n$-;7+uBZMjP~Y_s??e==ek+LX4wkv635W z&X(I`0u%7se7Utq?wAkv!j}8-Q~>)e%iu88{_}0T zA56rL11F`Kk^#>f-=>zk{WzqeMGAw4m-=|h{-daf5C$98O(S>Zextl30>ZaMs#wqK z8lx79Vpto#`%Nnbx?@18?zIoD7%RBl>l%g!3|qKsd6~vCbFTl9qgv-rN(SVz@j@Ba zVyp`X?&{~5((<4Rb&I}6J3j?k>`t_v>moMNM%Py?+IqKI4jxSbtb~Eb+lNa#+GsGF z+m;%Z)QXfAET$Z_!FNiT6J>aj=tO=iKhHc-^k5{qp5JlmUO^%!mp5&+Pc=rxQ}>0h zz)OZ0p$M3t0)+cK^!)7`cQrzk{oC8x9+<0F7gs@E5pUeL3j%vK*Ui(h`HKmqgX}uc z+RLjh7y>mS%#O&}8Bc4~_z~?#Y06%-TBiZ$GRN)M<27j(oJpqd6=WSDZieMvr_%r- zrTv28_jcyYM;yN$j^BUzfL<72&D1->l-uh9TqdY#&wC%%^+$!ZKTafxCQedAwp2~& z&%g^Y_qk=EYvLk@|AbLFoAg{II$oe6_s|Qx^k+xJX0&FJ0}(igAY9lZ0_7u9Hahi1 zaTf@1kJw`Wy5AnzxU&;Khfr6&?oW47gfX?;5$- z6|nv!O|2ImUd9p_k9cPPNe$jSVstME(BRlPXcD1J8jPz^_jdt_v*06nx*)Y=?<-Bh z!gqnDj30UEJVzUXW>ST2l#DwzNAm5jE8XrKWxiS%oDTeFulfno-RfgAyjO55A!){(o^ZokX!@j;+wsKJQ;BsQ6XboVd_Zn+n z1LC)v)VuZ|eTn%xq-bD^)B=YjQxZ*OllO&IbGRa4t4BCWTZ#eo^(3?dQ`l zb<2yJ6rY-zC|hJAgyU+^%F4yZ z^LYss2H5klGCVIGG`wC^_7huZv)exjEzt7QPwXH4wBXUPO;~W#~bjw z34SLcvsbf{uecc6#n1t_Kj{(_h`||V0TKa^mm5>#aQgHEOtn~pCGf@%O z=iSwKv0nCIC@QS?ejgLND+Xx#ugaLtYMpw>!)8^?dSqv#ePsuW&&$t$f9~Lh5}F`0 zbEImVhm!l2x5*m=|JS8XM2+{=%4F2c1^EPX z71_jQb_KP8C?cdpDO3&W=;cK)r5OP^Rm9HX(TR3OgLB&}I6VwyQiSeqiDQ;UYlTcP#0= zEngIZ{`9fs**ZqzRiI<6Q$5sH8m^WWusu((0vGRYdl*yMw_~{9hoJAN=Ps`2xgWH; zhh&uOdcGXg(FbqvVK|~fh(4HN+xa}vIyjP2)8Qd@Uy~Fs1)Ry`Q~LYkb!f=obtrVt zMDFvd=fa}xJ1h=ukd)Lk(bksUybA-Qo^ftl$;&D4cCz#F^25Pp?d_c)cw~96|1Pit z6z(r*EGo{;sL$`|%1(IIf%{9JZFfQja%a;+S961R6Jx_E&CjvQ>+2v0>#O%+5f3Iy zwB-%&0$UE&8v!|so7Z!mg2JzA;>w*CuPa->?yFb^TX0tqSKsIJPI6)la)?aj4&#!H zo)(Y=xGBZwH+zo|81hWDdNPV7-hK?Im1E<@HMN^^Mi)H4_tUi9-`b8N&n-1{+5MoI zYxxs!2JeTNdnSP@cpmpdKKp$b`q-Un^_%ZsPeDG42`#th2-_XPu2)^=`mdvxyapR> zt}tadq1|Vn*ZdmuQw}a~{}A|^DmZ=A4zne2w*4W#%#lVB`?iw;wwmyroxO1C5T+cc z0iti?1J5NA2k6NdS-B5=x2ylrSgcl`uMY-(zntRxYoCkyJ$FEUy330ja%ODd6PMig z^FUGG4cvbPzegdx(DY|evjw6~cNjeP%uS4=_eV?4!T7)BPphz@;u4F}#2RVcd~2zH z3_1Gn^@bL9dNzY&+fgPX(O4|x+53EC`)$|_zsua2cgQawB8&jUvl?#!&Z0MSwd?Yu zAg%~g38{*R2#~V&^=zfWA*6aFX+N5#k3xZuwVx8}P=1emP&%{9dLnmGqf!{y0c;+} zuBFd8f|JELc+h2ynrG+0{*=A%nbrXe_kjSMC@sVrZ010rGc362|0Ho<-3&20eZni4 ztd%wHm(6Kxxa#oMp?i8{a~n^>E}EaGAq5g?JWe;EKrWSN`}T1C?IK2;_31t`wHemS z%&SD1BBj{oU>t`CozK;CK3Hy6CM7!&6WRl793nB)t~1}73*riD#rc@9Upgg3#KgxF zl=FhR!c)P&Zm@6R@Ub$>q9`dT6$TL?n`F}A*45s)J{r-$!T#|QHWc~D@#z$^IU->TH z>2S%O5B?0o+~VyZ!~m?!uB|XtR#)@cir>4iF1Y9CA1r*Ld!cMNEmd0CY*or4F~S*t zB`5uSKd1Xme{zOq2vwgCnmDQ_GRi}Md}dg4P|>4=2$iiz{0HC(?hm=W*#HO5M3_+c z$pkR;0{M)O8qz!RZ<*xja5uh;f7(loWcK5Wy^_E6!XW=!zXE}VoVH0C^owZd^E+m( zI6lVKMCVzH&_lw8hJ!?W;NI?YVas%cu{w!n?(zGGv-*#4D(jE#lf-xvR6)#*K$Mh^_g|<*KCVl- z^IzWG+=2u)QwZeSLs%2N7!>dZa3CMLvY2=3&@)o@dY~0B8&U@I!A2l|3Ao>F{4PW+ zN%GDEWB&7q4p>E!+%M`WltEWfYCOkJ)TbT!?A4tJr$myYxkoeQAYJsSsQ7kaBvR0S zm4r_gI#N6m>s{}N%U*>F!dmX^?bEWoaVXdA;I{U#QuvqiNwJ1Z4Q>=MX z4}07%Eyd~2wPCgk`X0-+z_i9{gs}>D2qbQU7#YWY{;fY@ERhF!VRrtAC^lB)6nJ2h zm8Zvv1I3YVp6eTSv%4topQ2CKTM3 zB}Ait+s={hKz(CbG9-3inYbgRkB^_0C1_!GpU_=ZDy0+G^I*Q73OAvWsIsS$h^$PE ztn35>!>s>(nUt5%hh7JNg&Yy@KHN8cF2a_Db!FEugieW)w)K?4@O2pC$F&xE1-?Xc z`I$E|7=ddXW|&4iNr(60bMA*e&&_21T(taWsgoz*o$*rqv>O}jtNrGR_`@!$e-{!%u;u7{=WbsfIrZPV{4L>K>pp~LXqJ*P)BIn7)FL{f?P0X7y`PhOa!U)N_+9zz zBQ0KHeyt#haqCFkg9UX$0as)Ix8`R84nkRboE2>6NTVxx5vK?R7-9$0mz0#|+@5v; za|)6qX)WGT>yU~B@A%oQR^v9ka zB^HEKXyxg#7GpT#(m0~q@ml8l!{pB6n5Pzf^kr@mR??Eo+u;}`3_BYb3kr=xu9+}Z zF1VhwxI{2gDz#+82O0)t0jk?tH*;>$aLp3>S433nrso7UB~BBBn5_0;8o>;29|bPj zRR6gevs}sZ$l)_f0dKdeAZSD^qjFe$22deIt?%5tyk zT0bn^j!y|U`)HaD-}e^ZXB%?h!~>u7hDQc#Bi!@0s<#>u^AUT@<+d7Ophkz5Y;IA& zwbVC9H359U`%dPaEDW$Oy{=ghFd1fsRiCLYB|ypOs@{mjb#5T<+?McJ#5l{s8T95drPlfijzIKN&Bi;>u61AchGlnC3>=0a@i zcd5(q&bhOv@{I|0XF-E0p(A+BqrDN9=bt?JOvkrRd%o}1G`Ejy6?^@r#+I#Njj!9< zUz@;ir)Fd;#5VdOT-^+hJRyb>P)6S`?z7e9fx_bYNy7s8=Mq|qG`ewTu_pB&{@_@N zXi`5ElHz=YxuFycRiV8u@9oOuHTEBlaxCC6D$agxL%-%(K$o<0|JBqUjw}z@FB6@x z9giH{6pYmKn_ptxZNyH(CIJw1R^e&n%jm%{9wNQ8xZve`I1Ln;P$}K|UJ#zmxI#m8 zt1lsPHSBfPHiv9zH>n3r+TE1XMN>LH&6<=5>s#Q()V4YD6tHb>ze%>fnjH+rhk({^M%|N$t&{y zHe}fYO~+KG(fwuvm}J&X_jkG3gUgAk^c*96-yb`F;!2=x{QmyD%CM^mwUl;Bt<`J? zo9Pp35K+rrwvgZoj7F64SdBWZyvFfw9Zy`82dB3G%)9_&=$YjlhURE}eGvFfus=VD zu6sX(ldA9`j1WwE`l3imS_0!nejUGW=xNQp{s4y6`OtBBg{9e$X0OGObG7hIJ4}bv z9SYdxJN+(s93XNa(4Eh)B#NI7F=TO9?51sdhy4QdXDrHfAZ$pvso)<1U5BMpTK$rL zVdL7xX*7jOoA7hOR~c&k&xO+4Pg0i|0CKB{iL(H@%=@*HlR((vXlmkFJ0y!Tk;sd(vKE%%Q~hiGo+Oglv?mIQ zEpDZVS+bSLG%Q1pZVCxo+n!BK6#)w-5yQa1sYuiygxn^DB9cduiM5;AdKEGqjl<)b z?soweYHo;!xzU%vzqC60khyp@p%g1D6bkfHo5(rcN7iv{&D=o8ie5+Uol>U^^}$6uMQ*cqyyd0OY7s;o5ivQ-yvRMIp1N& z^!P~-a2Nj$ng2Cqsp;6$_BhHYc=cE?vF0@p z3BECw^PBJZo1qKX2pjt}yRloF4w+ED$@A9oCnB`I$abqq87PfS86HX^%&q==U5^}< zoraPU08Juosu+Eb05OUMV)$1`U{8;+Pwo5Jfp zSS6}ttTwO;{oR3IjJdi%asj^jcmJy%P1Q2Gh%`l@a(R7yaxzi0{NJU*x`?aU@c5=8 zZVo1uHu@<5uFkO(Lde_!KbDQWMrO3jaVfbjCp1Q{6*Y{2w<7m9K&|KUm1USAHPk#b z!pRi%LSo&>bgAghAO$AOHvDzb=(AP&)ub#iSFx6$F;_$Gg>h@?czWLcJBP{lF;lMc zmooIY@{3uOlG-x;MT;P8w$Ho4Zowb>%4mgAIvIy7JiE2;ugcOjcSLm_hQpN%D9ZRE zG%Lxk#mnFRK5D~{=yBcz^ViW|N%zD_n@iE4%$GaB10=sH<{Ox$`f9RgXb zWXB80Vg-TZU9Eop33RTi2p6`Q06;$~CQA{M34!>?Nq8UDP~?y;#8IKK>nShxogRb74B6&ClFlvB;^1Vok&KiJlKbt^0A_@1!`=0a zr;(_=GV81X)^)j7?6_(gkA}`%>Y{1fn21)jnUXzw`5V~cVg}N_8{-{r<(b266OJR- z$C6{^L1d1J{M)#*?R>tA$y%LOK0?iG_-5IKa-&>W`}{ZG{oYD8huye>%265@J;w{z z#ww$)h5`?a5$cc59^{~g-Z;+-g9_k+-1c3q{2TrJ)z7&CL!ewIU<>-43v-O>Z4f1x zg&O_x)7(9N45jg2aMb}X;PvCk%`J5*7VtU81)^V6+gm#q$yLn}HK+m>T%So;LDaQ%Czh`MIg?NzHb z-!FLbq><5zGvHC8gqsU$I*)7NdbK*OTVuX2Eyv2BPM^c$<@cz?wO?_NslFeb=BBMa zbY4429I~q!eJ^!WBpGSD?hXqyh%71 zIGJf1KFaM(X!xSz-2CG(1`dT3Cf2P>MRZ`EUv`8o($mMyQCTiGK$((S1_otS4q{VK zrO~<-DIX?_t_BD%{NSQvEfdD~Vsf**D)DgE2>EVt zT!TyfXo)4y{H3j%GB+xlo2l}~Nzw^xZV7$^xo&oT~7FcA3 z%xUPceZ&w-4|6dz&<8=+%})?xhmqG;l8}Abx7qoP9y43KeBd=L({Ue6a()6Lg2Z!> z<0e5?@9lOv2qHFA2>~6z!02?JIRkwVO?v%bX!FrW_pG$^7F8MX1H5Z}s}LOZllvDR ze=pm5DOw(!1q%#r?gOv21P#pE-uoHlSyn2`FRhoJUfWTFyK1|K?LqyrMcit7Ze@Gy zt>>ru-87Cfeo$ONJMC{f+4G?9tAH%HuYEr)&eLz-Jd&~x&adxnq7=Br7(qK6o1p(t zwC-f_Lt|fm^i|n8aQb2!TAr)B>@~TqH1Wbw>*m1Ny+Sl78)?0AW?zvxzw9h|WKIT~ zR+jgYZ*FczMFTO=BM&B%{8t;AWN&azCGSHm;D0lQ`}^bgql=+l=mk`kC-UC520paQ5~626~dAv`LqEnCkXhGAuR0 z)xg4QOww}lXw2KelZ-9VUw>yCFtH3p{GTx1Sk*4vwMJS zUTdz2XKJmndDfYk8N4LGzG576xbB%>N^7#Yg#`U&aOuQ^{5A3~T*yqQwe>l?`kD0m zJmBEIeBWCb9Z&v*|FX;q=3wiZJM>?~`}bu2K{U`%-Gf}|hxmR_LiW^s56sF$WT(EF zPv2}QIVOK9!o4$?be~o+LNCMD0bkND4N;}An>%qPx1D4f%kaPMkXd6 z^S+IflQMa;JP;7`fk7Dw3cVsxvmHd7WYNAfWDQvP$3(~(x@I=4t5!0#eMZwxl(AKm(!`)i#VIZW$KpM?k8CrOzY4T*Zl2{i3{%>jL|FWx?ZZ_oC z>HaLkQFtM@YXmTBHMIjTi>Zj@1@K8ob1<3%WdAleowi_lO?K$_?o&4vRUvjZ1;2Fs zbc;7SnKa6Z_<>pMOMS6$Bg~Mx9za_Eic+Qz^-T3cu;G-&%%QI^5tDi&j2+sa)0i*U7Fjb#2qS>{lg7gCY@dWVQAv+XUJ(6^#N3Lrf~s`er@?_wATD zC>(kbN5_1n9o}SNmZRH^AI`L+twm$@jPd&Wj45_nK0~c1^L8RsGr zvxP@cS=Atjp~>-{D5=HM48K-wb-pQzlthL%QYBFQ^sNudfZ16HDqymCy1)N6TH7mJ za_E@Td3{!U0cF--=|&NOgPIecgo!-}Z#5siWU4MzNd1MwA}f>k>evi60!x&_tbpb> z^i-1rC9vJq{&K!~jKe_+H(UE@!S92GEyuVqSM+N>tey>u>O43`j#C4eV}ra9vH`mR zKJS2;U<*4~$LRQ{eLo}x>lz$llr_27izG}p=byd~z!5Hf0fvpm&R%MwH)v`q3Junh zaTV3=Hg%Re#=L>T@%5><#5_Da6Xz2FoZ6Ga6dFwp)RI_f**S}4X0w5|)PI!1{g<47 zmahS%S$7*j?9i|)|Fi0JZBGlRA8I=_&y<=*Iu3`BaQ-sM%GEAV3ARA?49|G?rqy{B zkC6#Yifbgxk`1p{fAKre48nyUe4k*hbs!X42Q&_7u}vA*Ay&ab{(k$1iJ~+3&vo8I zL>y3)5>Cf%7G{(-tkqdYra59gG`N9*H=N|(7A<`P235P~|FYZe7#*4UmQbD6*kyM7 zoKaMiLVpv+3K$r_FVJz1Qy^%P?zy@LHt_0L?JNDMEj@B{)DHBcFf=dJ|Clr5c?%;eDms z3Q_u+CRLhqIyH@~)q==F1SfHL0`c*cmc?k@G=wnFqCSg&r|Mf;TU$e+kv8ckMC6;r zh8!LrZI+Msp3o=MAaOke-~xHrGP=b_)ng63b;NyO+1Yhg>`;-RRj;Wzo#=2pb zp6cic#8LL+>;qXZ-v+U1qUmmCg~7pZa4mc|0f4n^uk0-Ga-&0(;Y8>7z@&F8OWCK_N6V*EsX@iD8-0Q zBmx;^0sCC7w}!(0+9Ht9yxpg>nQh9hZg>6VY85f09@DH1(6yW!|IY@<(zpXwcw zY5!#=dDBI`PAx{9FG*%K-g~p`RQoW0xsI=*q2c^iX{5 zx-+&w8$mv@XH@*swGT3u#K>qEUvO(JEZSUOG4w{_=`)S`Em+J%Pw8FJ*Y9Uz9j z(uZ`8_io^ZuITIpub zR{YCdTeV?sv468vRT3c04x{*TZt{tSBK0;6r|;Ygk8zdog|Hehr}9-QW&joQ?VEy% zh7t-ge?K+eg%z*+_q+q8^;kBi-u7-nnF$&(VnDaiT|t?33wZvm(dO&*9chs03$3|{ zqqy;0>Rk5H!(`ah9j;E{vEJ7nNw1V7%0(k*MSDbA{Lk2mhGod;oq?F zR}gMsOi5k|BfhW2bB4K?B%0^Jd}-LunorF6swe&^?&rCu?=^t~t)o>SS!b5rIQQJp zP|QsmO_Y(2jX~1#SdFrd&P>{jf$r$+Vof%O(6Z;5HDCKl$QVz0zUm9Caq1!dj(Xk7 z{JOa4pW3^>$Lj%4Hf1rqk2wDF8iJ!#Eo(p|_{=wP#&cJFc63m|YAPs!%xbxwT#bEQ zX8rXOfSj4WQA!eH@dK~y6-mlU*6mGqZzgH`FlVU&MQ%}>DZgK_Xx~REMnN>wL<8aW=$zcTvF}jz`N$h)V9xl zc&fb9Jx*n-B#pBX8nr-Y$cgoal?rc7=kI{Mg&^9e(I{lpcFr@;R zG8!GG*l+;=6Jlqs;hcYb!{(weW-Wd8w3oFpf4PyMw*Yzu!YcG{=?B!V3nSZJfJlNh zKwI(|Jddf(Wi-(S6Y;MASq0eR>^<5E7o@-KiJ1vWSVyl}yQFW8Hx5l^{zB^nh6TWn z$Qk3^!AL=`6<*~y1v8!vE~%<1TImioCVG!9<0U3u265U11r!U`<7WshL`tRxd#LmT zwj^;T!`ox<8}+`#qjbqFc(}0N*s(kkfSFb;xfMOKGy`hyF2lUhnUJp5M(u zz-DSii7Y!A;8O)#>I1}UPu!n_tHnpcco!>^wH(gryrtqZzgPJX{W9}iFj=Jb_;qsK zt5m9KlGS!9aF=GCl|o%=U5O^aKrF9-NO^JAfCR0uB}W#@S|lNIH(td$Ccq!;4ZtS< zSQuJlkO+W+a}}&Y*v~~#W?^nt1Ts{u)@E^xoRPZY0qYZ@ zaEs3?AN}p!{B|I+sG`dh+QH^KdgjUGeakyQTm0pm3CHAMafksoy}O`@Ou?)If?T? zaAgo>EnVkRnrc*^;+F4(VzN@9rm_{fhX|-=dh&^egPsgdiDKCkaM^`*$uXNt<3kh& ziGN_lfJO3Q$cSRSy*`8{nl}-`Me7vWtv^;))R1}Ii^LcsA3CNe|G)U|n+zzcgB>3# zDoH%!hfGXe6Z%Bul)6R~3UqI9s4DBDBQ}NAeIL|+f5;n!6^;cd^eLeo4%a|MtXi*x%9cQ`1*1c z?f6(I3IC0Kd3LeBwoLMnSOgl4eRXMdHTs6}_pq*1s$HB|;L{oC?*oxL^m zEd`{8>EGsOO>lLW@62j=7+r3AkIJQuYijD`+9-w1>1$LdYpLTt#$jMI`O4XFm14*h z2_ZTC+q#%5n8hX3ID!(*j~YEa7v{Ifl;s#tw)eLgNZjiBG!iK%qf&&H#FJ*cD@Gb@ zQvDQbQG};E)KW94pL$qc@3|X7O)Z(aprOsLe&hZ7QGzCB?iCxplf_w3%~8_epc+)X zp-R4dq-ThVana=Sui2)s=0b5Jx?hGhc0$3JGjq~rkYyfW1dMb?A3gt|OHC!R4Ge>k zG7(K+@)X1{Dk*J@pox|UB~sGZ+@1WH7C3$e3dV;SaW}gcX)BJQXmUjBJS=4BXJ8do zfa#(QQx~MT6$)1zsOU~SAL~Xr%RNm8b59avYQ^vB)DCFN9{jUJmni*`C%0Z4#ZjSR zZ{v<=C-;4ezS?eD{uZ9?3**;V0`{mR(n3S|vI=^c>osq!#-AJrl%#Vz{b49sxR+}C z2D@SmX(+_Bp=g(j^$fN7Qpv30ftr3&N>~1fkfcBKmDpU*T>mYgYf!P&%~=vJcu6$H z{TFw5MpR=R*2jkYLW1>OiICA?V zHW8KLYeA9x+oxn5yu*ndzu#zDMJSZI?XJQIiPDnN;%_W4L!DN~?z3z2qN#egpT)4R zKA!~F5P+rUHEip%wEMHu`u{v`$Pb*2N2$?mj4EZB6*h_}v2DEv;r5l~X%v*2d`Y%B5apR9|m#nV%fpmhH!-!Z+=lbMh` zde!LsX;;kvLeE|tTh3PyfkkfRD#%*o4&e4l%1$|Fs2SIkd$e3XLo|hWY0S+p87ZwSR0A(PUPG5wHn|aLW-*yYN%+7M>95i(hPWepD>Cso_JJ63kw>D zlx&;!JDsMX)+VgPq?6nGG{Gzr))4yz<-(W>28-wNE#_?}A#NYpSqhdz79at1Tay;c zjAaMM`)|y)pBbd(mcEY&dZAn_)68M*;_QQmgCMdAC`VtC#it_JoDP3>VC=j^S_PTW zAv3mYi=!*^StkDg!o=#_DZDIjc>NISBx6~#v&(-P6yTf!0xywej34}pU%x*$7_`J6cySz&mLQdAc`)ze_$;F%l%IWnpJQo5>u#l9IcR@OL7%ugRK2>4 zYjw*hW|;sNwxiuZS#)bcaC5fE=y9ZH8w)jAlRph>>iVP%{qwtOXy^d)A`MTvzhfTG zz*k(0g9dO`E{(#-?@*$X@-=A04uO(@>a`eQ20TGocP`DwaAIPrg-Wrw2#j<*MHbw$ z^u#%^V93rM624!5uf6>SH9zX5v&KPe7c!>FNSz#Qf|E z3fQIua+hU6g=NxlPp;vxyYxKd(k@0s zM}K&`^x@;+cv|dwZ(IcLQ9`rb?#c~Edyye?@8P`$98s3TV6MG_pf@iq20bc`0dm-8@E4#6Z$z}QgsvEvw_1hRhDFJj#ir$9{OM`mAQNRZ&kmkLc5 z?d2l}E%@Hm`b%Ig_kklYT#3N>n}sqrRsxX%CY1zOqwcF%I3l#3a^EMFh_HvXgM;@+ z87jpfco`$(vDwEgJN|igCAPNup%V(^GT_x4lPDKDC|Ou^nz1e9jqc>ycJnBRd22r! z7`+1nC-(A7WuAMzgLUY^iDf-?#GP-LrWAo1CpZEarfsTfz-$|aWiPL3TiB7SLplHA z0x(cr(fA_C6am}*1_!N9Pd7`oU|%3q}}qAi{F~L$tmNBECq3CLf}-NM)UZqHq|&9eF#_t%iini>XMtH zzhX_bz+&Q|Q>Bdd>!&xRJG})|g(hNZey$S)Ksg_1Ks)F=HmK|UKa$n`D0vHvGzHfA zp>qi8*%BdwsoSa0O}s@){M6vt zjVl?yGR}C6G+c(+caS0ftX(jfn4n8rrDg`!EbLx9_XikssG&efwRAd=`0+LW{vP<8 zj~7hTk2{}lcKZb`iPcJYT>Gx4+d+JOf+Ra^2Z-7{ZQHA{$hnlfR>iUW#CwA?1pjiY zb^Q=ZDk?m$N63SGA?auv?~(DdektHPDidPAOSYBFYckF-Ov1Gd)+eKV8j;hNV8&}< zsIt6*w{ZQ21d^zPBoldGn)QS@Pvg!r6h^X9Jxt;!h=-RQm%T7OUlAHQJT@5_6A=KF zm_my+YpTJ?dFS3onl`1(coe&_!K9Bn{S>y|A|bZL{Lg@Ri$0WZ)vx-TxAEe(83wV} z9Vh?0>~mRP5JViNBIH5&nE2&~h}k2`5ur-z8+gef9hwQjK-4~)TVJ)-J7`UF&FAq#?rOu*-uX>LvG^GhBYk;oYfq^L0YnZ?^NPw!7A#mq#PIz5 z{JV11tV#|xdUkeEnS$lH36;kXoPVbeRO$WJ*1i?ZYF4EnMkJE{7FBZ0_6nyRZ!IMk}1uWy?y-7X-*z4l# z`_BFr5Zj8^(cG>VFG-C!BF=f1RvaRtI_9wzE-vhkzU3sH5hddzSMcuR|BYez5kP&t3KGamrJrRIdUb`+!ypd6eYGfoCnbNP zOzRR)WdC`ECwYlK4okCELSWThULzh(V4mB+khDZvVOF~GGQW(BnO4iJXK{ISw>v`O z_vxYukq4)3f&cBA?nBkkutkVuXCA#?BDuMdWpHREX|#XlJ{oiA(-S8CZ$Lrojo9b-eZ1UR{G$545%H-G`USnyEo6hcLk^x$ETWZG0%6EC-+Brw zDIbo5W-w7w0F1hBw_}eUC}Id)S6gM)Xrqu(-ehOyNHmxo2)0N|OkJ9r%W8XGC>e%C z)H~;RkFzUbbDBUOeyqs8!Kc_w!qr-@u^Vf#d;Pb30z5nd0+4#K9K_i5ktp3nzPX}B z`D{PzpZDA3Qg=hkdWo!LpERd0`%7CjZB|hZnlwTUu~~dB-)GMk&lh>@3O(XE8N|1I zy|@dJY6tYgBE$plM9HF>s_$)QtYU_d753Z<2KD2_2vp5XLD+YY6ZRM>g=>uys(j@5 zl1v@-6%wbrbB}c}GzVpzkSzYqaDU{hfSd|K6Ff?=qny_-Ct!0{lsg&-drP8%99q9W z^lvDBj7D2eo~%TFV)bAtn_sJ+aw-8QFU8iii0=mO!7cdCFic-|Y9=#0jw)K5$oC2= zKEulOiN*6kKEcc$X!v$R$|~-MtCO2v*^BWIr_*B43DY`fv17bu8#OsGr3$UX-OoFh z%yQmUHY_Zw0VA~EH^)ewy0zdOFyrfi$a%_JKpVgH!%~R-4h|0d{D6xO_0z?o zZeG!8;ElBrdh&M0A5x?FtgN4_en}O1e|SjY(>UV^Jc5&$AVlke_jcY0`l6z4JnUOh zp`%Oaa*LmZ!)kUpyBHO&hNHT$kqO&&LnXP3+0fI{(C~J>n7PRWiQ!q{<6>Hs|2c_^ zEE81~7qgdGaEdoJ||;%G7ZgG zyvX;%!$$LT=6Y-tkALI-*zO`ypgNF3$JgqQ=W3H2FEk>`gI-MjR`#LeavF^qcsC)v z2UW;r1h^7Ea0dq3l}us~vmy@ly{l~8shqQexjBjJ>bGB7`$c9U(bAKhgps_I{-4S! z0Ed2_b3{yZI;*R5vSF}!0&C~Nu*kui1h{Cd|6Eu)P-5#&Z>!g`+A4wzMcs*V2rd(Z zh+bfZIssR5WNCsu2^yeuxCY*W82I#?AoD>s&=E8Q9L`dw*Gm{O`M5E!F5e!6DJDWG zFavh15R$f*I2-6W@m^nE4+klI$;=TuLSJSbiUB?55~5bu!z*ewBF;y;}jru}~R&z_uLKa{wWbk-O^Ds6eZRWk<4>s5YrV`prmau|WtJe4l}8 zv5RGMI$ti@wKB>)UsgBYXJWij;>{p|vGBrJ14m<*M2T;tx>u^15actOHxQRuK?Flc z(L@yr(iW{Jks@eTzI%~ttiAmrvh6mTW(;m51{`>AW;*sq^kPsFij$ixUY3k=$*HzT z@z5#sHN3ZwK7Nn6j2{5ve}g2xf~0@}Ph$lRQ06*9>f{HEBWeH()EvCKtH<(_|3E?* z60YIQQc0B4hzEhKXkus-X)CcrM5SHSz8cTl*4nx7G(E?WjRax)fhNzKYdLECU ztE5cFqlLOPog%em%I;FJi$e$%AvHEiH6nkITwSy@CEF-9N|>IiTHD5rY@Bai$~!ZS zzm-^n`Ma;Ef#mXb1D^zM5%$}4uY#S@a5`fmwitpySTLp$Y=(?5=VRvfT2shGhla%v zuYpDfW`;MSa@6Eiv{l~UoHUJvQnrQvCc^R#m0Pl%1D~A*=7#_QGSU0#NI$3U7IZ$! zX)N<((63BfUC@{f^j_d!VbVVXTvebThKAO9*4+D;k4&40I5?;}3h}yUrzf+KpVd%H z!AfiGDVSK>?zl5%Tb=s9^d9j82Cu0Beb^b)LajNmz*_m4 z%Y&2x+t$1%G|pM7coCjFna~*VcFv6i2Fu$-6nZ+i`Zmx1&l}cULU#q7JtAI@x9tZ1 zBLB_G0cKXS1sSTyWW7=YzDC6Hb7acgJSOMdpqQi;j44{4^i2#Gxf(5nYapH2JH%I7C%;99T4@*_8wK+G#lB;lcd)vt7d_-=6 zdqZQ(P6uf+YpRQk%KaNF3iQXjw*r8QtIIi+57{v@RMfOp+Vs@zG@O532gL^Tg^a01 z{f2e^Af=x;Xdn(wDO6Y(l(_h>*LRY^IKLkLD2?^Sg;Obq?GxKDBKB{)27m8f|7!(+ z0L0pWY}wzM&g5`EdtGMAY8yFKSaZ+o-Uk2l$8J%7^2GlRxZ9s_Yq*F1pwOhS@Q_-w z+2}aFDC*ow>--#jveA`f3n(Q3m0fJoK_?lkPp=9mP9jL!@^F-QC5sD0mired#>cXa z54VfpH=blG_bJz!a+3>oMUuY-hWrqabTNXz9|OS(NV*b1cX;_4*DiM;B7}96AXF7w zCYVXo$?!r9**!wwk(r<#9PSquA8P5e?F4HwduY488$J{kBhh_7w9;os_#G?wbwk-^ zPVGbaVfQ4bAOc5AP-qo8Lg^BJS|{c~*RjB&(LyOcx$D4`+iHodq6P>otpP~wlZ8s9 zT6`u3Inj6r}ex)-J_xR&VOB@&X-Z+m+i%0ov(w3`oqs4$s#MfWaA=xb(r`(2Z? z-d=yESd;wC!v)>CH{gdX+D2YU*=h(P`c1#?8(9&>g8u9s{0UhmE}itMZpSw2bv=95 zN=c$=!`W)3O0{BsVZT=qpc1mB28AKtXw&q51F6l160Jz`xXf7d{HQP#w>nu+jf0q6 zko`h{Nmzttxz5cgwe@8>JEoz80U4B7-EB4Cfegxpj$eop1^PTbkMDn0Hzl?^j##up zrCf;U*X5@MzvTDTt!1i6puH{EKSEE9QB#sIFC``ag>TbaNRT+{`xax+b89zhNAp?e zli4SjvO%0y0l&hepD~lEi7992*+kg$m4rUat98sTF%2#s+(G%o%QCqopb4UqhlfxE zUlH2IBIQ%aqwp*E|0D#dDw6dhrpcyN6>Iar(Dw$5#^m5Y=FYQxS%S|YS#IK?`zVr` z*u-{RIe=)ki6DhC-kRsrcJrb~aR)659h;;|j#-jT2wY&Y{Ib+qqjbLj@li=KBi)y2 zX@Ax0QSEuPyw|qe6*02j!Ypg1kj1C%V7H)RuZkAX#$Ts3#LGKhiiKrvmDNaj_CKXF zwn2!#UC2Mp?{;JO-fwa~a7H_gAtDrWVH;2)wh_@|%7cJJMJRT6$TrdJMn>qpk}(zF zYZLIAY5lqp@eqf~u`}J~>5WNA7~-x=0>@cSj{`iS;W;xin*0{2D5K&+_lXt;)q`m= zvyewj-OMp84ym!q*9{+5jQEuqqfhl8DhKJocZ%i=h`8kAC2f>OUmNz0InN+j4;|8( z?Cp7RZ!jVX?CYQGE#VS7Hl8pxCE6cS$ds&(#O z`w8BI+K~XIzF)425Jy#AF8(n`m_Xt1XL9yQZbQFjP;m;zKJj$s za`yH#sF8s3#XLdv#Hr_e7cyWMduvY!q5Jb^@u3Ay z?q8_L%e$Ae32Ec-5JS6=ttgtjrZ&5Lm>J>9ZrvwNR((1;>BUz&4hLL#qj9P;O>rGZ z4EH|;4OS4gWCe$?lvsm$@lXGu6aP;(s=k5{*mfUa70YCCdS`A@_Yo(QawZ9-Ka>>b)03sZV}a19+bi` z7}GNe=OoWBLQ=f~`CS0g1GR9`-_{@Y1QaL9$xA zyCh*Un04%^YT{nzF{|@EzAW2@Dec$1)K4XsvIIaYVa%L3Sw@!HzHrlEouNdckT@A> zJd#T|7Zd^szb`@4!^T<&E`eAqQPI-u2kPgR%T9->)(410rH89E2lni^>GV44op}TS z4m1$PN)jqr{(O$Lg6t5ip&2DI&U9j@)>hxd6+JYW=n3kY6n8IGoH#*#>7x9C)>(Ck zhq>%w5j$B9sQy^ENU%-Zs4TL4CHv=?3?peY{=SNYAM=MFYn^^q3I5-ni}cfMU4t?^ za!W0K_?<$>K|yNejae4TCP_4>1fiLgh^<(if$4e{75H3zrM zVgGdFjcQfGHsYycZ1TylmGj*@Q#$7?f_TX`_GQxsE_}w)5*M;?N3C8gI4}ax|6Q(` zP=Unkp#cHRVm07H1I^&IM#x>@cDAt*%m_V*eb++Fj{`F=5!*>#`hR}v35zg1`-|Rpm7%qq zNNn4%NFe55iq?s*XJv=qxj+u5T*jIsf|RA$1MFMB#&=l5g1;0D*)x8w;Cz|>l>9_KG>snb`MHLw{73Y_~TlUXK0Yb}PW9q)we9=FFNM z>#D0{9NEc~PGG_ZMk%tTXt#6A;Gg(~DU)s+m>Gg(+;=H|APoeE6q7mx-_>m( zMR2xDaM>Tk-^->VVjC-3d&+R&TU06S3+tL9=(Aiz8W}uO33DL6yQS+`1mTisQFyZR z-QWahs)m|oaWOUVp5|@W!fdyPu|5*1^cG96-2(du7dvB4OKK!YSnn@Zx>H7ZTC(B>-ayWR# z-tVDpmUL>0rIZMzZqBn;GLzSdzwUsKU>##5J@Uz5O;DASe`Yb z()YLXGpfDb3>0zYfz}!=O9g2#V{1u?_@UNoFB96sJxE`y$!j=C@uy6mt#31!$onUb zv97G^op#;4cPp93FMH^?1+f!_Q4(VtXTCC5$&Sm~3YxyQ;Q9_I7nAbck`#kf~p6sKN^GK0(zlOe@s z&@|Qy7w%ETDG*@>(F?dujf zKCHrX{$Q|iL86|Q_QJWnyhfD3uq~pUZGZoz_kDYEbo*ZN0r{%tM%W!R&*vM(&DWUy zAd!vrkL4G>cOtBGfFLzlO#wjYGSo&*7p1CHZRe9ZgmQL{QC(t0DlluOHedtPFd+x= zbEj0bYg%Z*BWeq$2}{+KjGZA5x+ropL~D|a$7;jPv=7^cDuN38s_MHNXd0^df={4d zf&P2NLw2Xc1@4A*5kg3qm(z%EqwznScNQ&Uwln>T86%W4!@((3JBWE7yBbk!|E@Xd zL_=cL&=hNBBRV#0B~dngkYZU*J3(p1K5U!Q7!)5FTxp21)H`CCR7PsU&1yK@v&_CQ z9_Eit$z^_1VkO(H%;5oW0x_3g6#3A zyh~Xo*5)-+MU&-|D)P^3nU>{ty0i?RrUJ|cDE@08|FD#PfQ%7I_1V1S5Gs_9b5X1I zkfuSk_j##omW{TyEGlaH!-R}n2v zlBdhbG_km91qvBI3$au_i$|31x1TTG7@B+EYx7AO4UxL7*ghVW8>ysgVw|6y*U?bN zlVmRp9-iK?=!=&L#)1YQC|i8HNqFNwXRR;&)vBufTD;krLNe0Mr8y~0djm6qfc!%! z(T3fTmTZOJh#4_KYEEq~kivCW)dmdi+hoa9jI;?P!(;b&aJMVHXTxh^wR_Mo^ueRT&CD?BBw4;2rPJ|q#A14aBNe5~%`w;wn9V_O8)ae+Jie%fNRVF) z%)&(JuNS_Xn0v38Qgm^rK`;f{AvSI+hc3_f3h^^np#4znH;#_1#6%gY@zAN3zl?i< zA)zM0%zBe~S$&L{WBqPZ=f$z~8S@Ii2pLrnOB0`siq+N}ArD&K&P(Or-&u%piO2UA z%oEUaF7ETNAwT)0QQ{^`8IVrBS`vuQmispFT%&>^H7lUQBZL5vy0nHPRIGxLbTY& zN1j5i7s5Yp;3X@eGD{xW;>gWc^pv@0g^SjEnouf?6>uQ5@n+r+PGxWJ)?eN_=9yqq zye%fNpn11<@T@53u8_+%c)qZ~|F!vlQrpuB@+x9(mufOUb_cJCIV+5-{qeK7gn|M; z5uo+-h{O#th0-Wl1v$la8jXWbQAF+Q@xCJg>;Mia(TP)F;&{MeYD+6D4Z22|;h=h-NBDy~I(1qXCDe*8haO zzE4hfY*@g(I3B7j4{K2R(;)<{MV!@CIpSlT;b$Q^Me%V=yXPXh8WR35kpQW&X_Bph z5=7@?h||J5{#r4ib$o+OYn#Vj>Ka6((5|x#E?Q>9U+;!EwE_vA=)?+dCMY%@-=Q&B&C7^6@!VjsszU zGT`KUGpgDLRN=F<%f7|+y}M}LwemHO1_wCPAm1H!Ey|L_bAxHWtJrO%+p13mmTFFP3 zs^J($GCv&UVz94^M9S2(Z>}YR`8O!_A5Hc(6R2{8q4oQ@1)g50M6ru4ea}V&dRy1s z{G@8rC5$B8+H;E)SvDyHr)OWz>vjADZ;sVMIrAPW=7qBq+^zFn&=(^^6txMGiD_@u zX_lQ|6Cp$_!}x=45-m=T0b?=4?feJEH?@rmVxIl`c$*%Ih(o}3u$lO`ot@I9Dh#k+ zD7%E#?;o+pXP3r%O*qBel(>e1IZ%~>fZ)Q!h4_^g4uqM%kz$c4Xu(%RJPU_BBw1Z6 zM6t_Kc?Q(kB0(@1%`h#Sug{!qBIK2yMO#{02yJ3*2}am^!GG|2_acughL=;vwICsZITUT; zOGc*4>n4Q_wVNsIuy~`}{PIo){f4mEZIbiOdb(F{geIuo=*R6mbt~n5w;Dm+BNr?0 z3RLPU(TS1%0ItxLb9GZ_pe%Iw1kXF2P1tBG`GDizcDnB1*Auhx-gtPMM{KlkRu?Yz zIkqTh=Djv;DUdNO^m8y;0(WO=w8GBYzV9~30(G(3w zu2Tzp$K@vty)rmq`%qCwB22!Y-+OtSlzDbp4?Bnp=>I9qU%zJRybWRx9YS!m0}657 zJ#?)IZhMG_R1)a-RFzOMNl{KykIp-~Vu@I}n3Ppj+1S}rXS=F2HO!a|M^F?P`anA- zDObanVk1GQ#wqOg`-k`erOpY8p+EJ5#6^199=p*Vjr12rM@MIApIoyZi$DgJl&-p( z=`L$RhnEpU67a7J(^<`qkw^ubMsV*_O^Cm9%@X)wz_ux~6iVm~+`Or>|T z4^~#%>N2Ul&AyknAIgZHe!Z1_w5ye)@&bi$>pu!VDI2??QJ-_6b>e{T{Po<^j_T~G0K#qs<2hMz;+ZgEft$28;u^rQ$!c3vP!x1y87w}T>X#57mke@-SjmmE&g!jh z#YTl!TlJdZ+kZDkSuY71zkWUY`LOZbd0yp+#k@5!D@)QT)mQ}>>Yw;A7N~2v{%XT-R+DQv9ah!((zIctH{t-fH>?kF$o@ z*PcKocHAm?dEXpsMl6#W%40`lX-0p94FRbS0^^A@$!@{4|?$n?ruCr&68C!FPC zS8KKjTR>w`rcU6F(bt-b(^^$-+Jxuz_kH8Tsh&`O*AIQuH+6I1q{sS~C;?m=0zaV2 z(vs@Z^2&;N6Zz-o*06UfHV@8~Hse-i4p_kJAD`}J>CigfZ^!%>uIG|agv&}C$BW%X zhXu3I!X3HgQ>L2z(urkJ(&#?)yB>aAdOeYcVfz0^EC2Q1y(>Y0A(cSo5T(Uhns0v) zWWaoRWNveQexauRL4v3Rh;GFlwc6i@5dWCS`|<_)xU7W4oFDsl0|o+ZN(MSif8Oe= z_d3^#Fe74BkS^k6$MBk1%>Hux)G*Ouy59AkDQ_9*OG`8xa5>QQf@ zDz9g<@^O5*I+D8DB+ZRQd}a0GIHGa)1Kd&tD4AiY{JSn#m0C!4P|4SV_;UPMxj}l`$z^%4Q)2n(=r;* zQqxTwReliZfu{dvia;PSe15rBuBO49o;w3UF?{VzO<&P0DM~z^`*{~;+%`LTAwrl{ zbEG+@nw^88Yj2x3v)znSM9T99Sv`1-NWC58!_;Z^pnpAi)fP9D)%fAaY^S)lzcf09 z=)al%o_h#EO()-H1|puS@x7j6%4KNB-6?~_i-|h@7anZBC(03iofWlxEdF+r*z~Mc zUs-9Bp0c2FN&OxDB}-HNw6Ma?Crw-=EYUqGbG(n$-A1<`^Cj3EyL@;X6noe-J4OvD+V?NyZ1nViwrkQR z{qR0Be7Z=`o0;K4Z`$*e7_X74nm=u<)m(h-pu*D9r?shSGX5ortaVO|QWP(~3yqF#fgQExYT_b+APP_qm~#&MXYiu#84;04;aZuk zJ|4Qp7Et$VWx_6)sC!dQAD~GHp;G+fAZrf@z-9ZkERkyr_MfP|x zA^F3r{s!st+=_tk0vl#2a0mz85QUUhwx(Ub!hoCrt~#Nh=*7L(RFeD43N1XX5HSzl z?07EP?=;!g7{BlCAm8IYxq+m%#oG;p)La+uLl8ytwocc#c6;akfY^f_N;7%o02a|J zYHoijq(a0x6qq`_2#o^%>c!)`va8B(MKh**kw=Kq$A}eJWPguQ{dK=UMqmJQy>%8j zVMdRhCP5?8Y!PjecuI#nHr4zP#pkefx@oNO;_2V#muL$c*`TxTc?&v~YHmJ$F)|}R zq~HpB(sAwv-V@OS?!V_{eY8*=d@0Wjb(xC+Ht;MJlEY)EJVTsCv*>QQP3Q{oZ|en& z;ClA|#`$CUv)nzOUc5ARl{>qwruzGYUt8bT&tEfZ?)zCW=`$lH9dlOwxZ(7)z3e)P zaVGD7q&)cbz)5A#qejIp-nO?>uWmZ^>eo@$^PA=ioj&WaDS*B|Dcius!KdZ)jA-gmj$s_lVK-&s%P&m`PuX+Z@ap~BchkLJALd|V zit7q0xRO?@Ej?ey4QtN*qsaN?-j9|&hl~|Gwe{xyPS|>LNnybo`>)4N%Kl7YcrW+! z!}kB*({{buw0h;~8|U?V*O~`S_ytC%Y%#`!M(PT;axBweIsR3-`QV{y+cpV+)Ti-gf5`n;J1X#L#ZBzrxjn zil-*6tVloIKK(t*&fW9Y$nRM*b<(1>f5PU6-28K7AtSbQ%fPaTB}i7Lrh)syty9A7 zy>-)sw4d2Vo^9u2^AHqOGbqXCUbVvI?IU6Anz!{htZHt4bTf6{s#{z4Yh@+9Uud)N zr1kV)+ZJ?rg=pJ3A`P^{6R|_WX^tgU7B^-_+y8vHRyUa66>ABt@Zkfe0snm#9juYk zaNw{i1N-sAPr2V>)!pCV@Jev1dxBBUp(%40PdY8p_PJ_K@V2AwY2Q7~RIYuzU*)-M zNd{A+*yX;&Z{gxh{zd|8WHCJXV58y$VA*K=O!)j;?!K7+d$QYfre3_hr(jLShfh28 zq__W^I{nJ~+!ZG#t7;V8a^HPBzt2+nXvW#6XOEYQsN0Co;$A#gKK{M;%bJ+{(_Jkm zW~GYTTZ-#n_^n>An!CXx+56eF74PmHIQpH5`QX<&%_QE?jlPSNO6#4c>w2s?a8OXW h3$r{w5I*C;fA+HW7oC5F^zSkNfv2mV%Q~loCIB))fOY@? diff --git a/platform/android/art/tests/FirebaseTestsAS_2.png b/platform/android/art/tests/FirebaseTestsAS_2.png deleted file mode 100644 index a482a017858b2ab14e7a80f379b6eaf1d864a8bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93449 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 z 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�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 diff --git a/platform/android/art/tests/FirebaseTestsAS_7.png b/platform/android/art/tests/FirebaseTestsAS_7.png deleted file mode 100644 index f7db39728a82b19cbf6e5c70e57c7af4a40d4c04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 221809 zcmaI81CS=o);0RH?e1yYwrxz?_Oxx=Hl}Ucw%t8#+qUnV_nhs;tV& z*pYkZUTd$aaCupAIA|iVFd%r*TgI zoJ~L6Ng;qR3DH9V z6;wnXi8pC`clEnOzpG&*tIM9;BOn2@`^)8fd)3>v!Tl4A zXT9=;iJN)*+UuD4muvTZ)^&o8j*iKbAx20qx)^}i52+VYACv(Y11Jjcf1dj5u>q4v zDqcK3ws5oCt`jevI`vrj*E#)j-%d*)K)SCdYkcSTf1d4S6$RL`=qhnAGXL#ohdl-` zna(1E9X;IEP|V807I};FhHY!7&HJy$5|P~2*4C1ek?tTQVzCwRJ&gZ(b*liNC#3@b zL2mHRkW3*@9^WnuFpY(U0~>HS@cuJeZDPB-gE9O4;nf3wi7M5|CTzCa9j*WA_olH+Ob_6rl7up*$ARbgIiRdTBIYJj>TJtEjf!K7|*wBEx zP2v(m<@p`enXTMfVm;4_W3lDfeW_m2jA0gt8n^&55`~bCMTLO=!++r4mPqvjum`)i zy59Hq#-l`wC8nmjzT6miUV`(*^@BW4z+yalgiAk7Bwbn*zEv%qTQKxBxKzqZ?2qet>~FtbBId|99|pGBK0ahI2* zmQxBWlzr7^?z-&4&~<6Y{U@r9PhU_C|E{o8IQO)r)-+NCQp_!c$mWuA(M zXszbawNdUGwI>W6ODgCXvCyHxQ~rPKe#1;8ucwEPk8jJAF0urZgdQCoJvKHLA0Iyw ziyyk3qrejzdDU&V*U3qtE!tL++fe~C-4YV?{^v(l=R7SGf-3N8rGu07u}o<1u=Gv&%=XdXhU)I zt*#Hdn+GMWj`v=uoUXS0nC(lgx}GN|1dd~q_x!}wF{~V3^z>c65_-j1h2>*6VYT>z zUquuYpBlvvFv3IidRFliD#-XMYHFAy zVC4KU*6cKCd;I@;A3X?A?V4Zu!vuX#5WUQ8hjnst^0J;UFC?U?3OpW9T4Le=%*1io z`gcuJvHOg4 z-EPw<^de&GAWF_wo2h4K$JCPG_Xj0+4wNCYKM!dD(UGS>+0utpSFZ=rGV8q$a=q9) zyCKWUuba7L#cVHcMQLBcBEscg(NRzeZj`ZAB;SFa*LIGI^oe5q5 z;KIlx!Y^f?npj-V7x7zlql9`Kp?kJe4JWLAF-noWOIhiNuSj1qzp|XU!vrI7=leO6 zTyDW4bpwlOxARFc6-g3a%huu02?jb(xN+w9B>o9OHw^>;+b#WS1sxq;(`g)CK(f?h zKMcLqRww4<-X9CrnrS~+FAN!byZuA87D*&|r?k%EeNji8lS)bxB3mdmH=>xj@>&MR zH!C+cd56*O{qO{hhEPz=7p#g{FEtp}g1F2LkY4+6uM0oBxFg_-*vPyFN zbmKWhcXbLv!$TuqcS(5xB>RPV{$4$~*9YRS?F~EL7)19)-;1*$dn95Ezhv~q;E(v0 z$?pLYFpQdfywaG<<}Y@0ePMzDxn&>9|Yioe=^{&MS%#=`tL1@?n^}|%m651 zyq1LcJ%aAO7@!*Rw4Lo6dz3UoK*wME@r-7eWbXYhNbyA1qV~O;SkA28$Vn*!oT(dL|VD)moT0#nzT44_cM)h56Ry+9C{zNx~RH#LL`@41G8H3mh$ zCQ1Z52o+ugLfYb6xC@8k{~f7K1p!g40!={I?X`sa=kLL;IhThd_Wwdl@OE-1S%>;D9+e~*_ z6MGmmb`gBd^^~JFP1X&*mzV!w+U@o(^3q?FC^U2R^)x{HNU+2QLx9=+?$RhZDnV33&(&)2c}im!F2m)= zl*ZFqqg%dQcIA1>CKs_TtzeaHRcc+8@j`iW>9gn&?e867#0|r#qND(kpk}DkB~W|# zfZy%&ntSaM{0WSIghHj;3B5K)G6=Svzn?{_uakQ|QGyd|v^tGV;2&f>OI3opcld4f zWIG9q-$LotHEiAWPYWCM&8#>D8&lA{zV1AN+>EydeA`J$5jE@pDBUwrneNvgnCW-Z zk=I|3+fdJZzDgzxf=*XH$2I67AFuJ|;c@BLH(WuwBVlQ0^xdI&@sGWFwv1U{7X#Nw zSB_WSK3GJ59(KoCY7!NO^XDDEdRypi*Pw7Jz3~D-SsuWD&PaD3`g7;f(VBLDt&i~f z`WzyJGf-(xcfPj7k{6WqR^QKNjj+{d^&o_^Sl)uv!*q}=d?le%?zeXhgy?sOjFbiy zKVBp;y*}}8C#6DStU5OfWQ3cwzCNsPqr}qUE}XSijBw-V5u~BXZ9;`snJDxV#N#%y zo@ILP-xj40>aA#89g5?Pw||BgUC@|<;Id~7$sy?W*ua>r>1t))Rdw)z3q3tbhSUZ2 zGdUJdN4)M1(9iDd92?}9=${{-uZ*3vYne0)^oTw(tVoO3ajjoLG=Q>zhLW>b6cqHq zC?W;*t@t8?Tv_pa_5CLgy+*Y2A_%ssCJ{2)@kZ)m&BuRA1zmti^Oh9KBgml2n(A~? zgTw5FHehBRprsHy0Nqpvi3G5PUdm-O57N_s@-nSBb*rQ5W7KJHEy?dyYHR7f{5x~} zkVD*Q-qp7N27@T+_hXo>>hA7F6@7hw7cR8hGaOJpYwY+Y&~1oG`+7+`0r*OuHn*r+ z-)lU~CWeoTG;z$ zXD6gdT1xSLnLg%Y-+K45F`j7qysmx|KjHsM&tq{}#PhkHs@J;G;}hR>yy3s@c}@Ci z^PPq%&Ke{phsh%KOwSw%yi{g@M`USE`W!uweul_!PE;6>&UD@XlEI!?K8}G5EK-^o ztKuedxbu=d!1hD0;_rT%x|SmU>^Z(tder_pyKKV#82C;@FSMq7tt%zDZuZ2FhJ#9+ zmGOfVop+jeQqx;eW&fnNJ)n}2(fdA}`XoO7=pkn=ZFaQveNkn9+|VKRvWC>~MuMSR z>k%I(_SX4wy!J|WUH^muuds}<(fYhodIly)D39G!%az<`(=yQs+-rY(AOjv)FZH+fA z)om7L!Wqc7dXxJ>CaLQ$(8cdo_e<9zjh5Vq0m86^eq7RdP6S>iRt9y=_RB|=bx5<% z2Z4dPny<$ry)``F{c4Q(Cf(xZ=$Wp>9G=g-!-B#rzsn!$eht!@5@zVp9t~)Ar2>*) z(}5*e1NG$Esi{Q79@ANsVVE&H<`kdt=^P`eXb4;fYm4sB{Ex6IS>IP@%Pxq-*S;T^ zqUWbA{2HDt)Xo-BgLoDvv6a0{wTj!UX!@}Rv=eb@Z`+&JGUEF zsl(lfM;Y<$;rx46?yF{eHUq}#MS3z3+8v|7bCId9URh3^-A2+ z5}jOd_ucB&==$HOi~)E2+`NR;A}w-k|Hd8^7qmmQhyp-M z-_pqZ{yL^60`Fp6>)GmVSd|uW3T$ph96j~pfY8y=avbe@2^jt%O%S6+t7wwEw3~@Y zgcCb1a2C&f_TAL|!=$ZsN8kF85Mq`zclDy7Nh9*gmqZblq@-i>k+CrP;ye+41ee>f zT9@^1Yb4K9#p-SO?U4}*K`x4N^=*1h$4sE8+~aQBt>=fXghGv2V52Qw!3yO>_4}L{ zOg679IQ_bns?~}ltl^D+l1~@r_P^1iEuugM-_}9PfwL^whIU7caE&MkMLcmE)*29c z#J}-?A2X=FVgS2>jRmj~+*ef+j2$>`e|RJeHL%bCh&Pc8;T%$4P@T(lb9*Lt_M{{{ z+XB)8ekbF1SH9RLsJS>bDpi6+;hA8|*SX%k!6FIr5V;tVh?0b~lOJf(lznWCI*@y> zgGh3Avh;=R$)DoDQ!v4emz_Wq#aX`Zsb|{F?+tOZs)m3aW50nHeW*E&jz|d~mOX3n5)VLXX zQufdlYmtsmF$)gMMq-}LCW-KWT=Xg z$ku$F79W?My_QP|9N{~Gzf^oLD4o8lj#F4zrt4=KYEe35w!>zbjwtI?*J&TtN}!AZ zNufHg(^4X}jY!C0{$QbW03>WI99%M?0|~TeonT51$Zk=dunEfGT*Kq3dpzw>$yu0W zx_ty-?+DFy+JIUT7TpV@q+RtsvD&4P|CpKnrFr*mIYo&Y|hrTOzK`E-2%a6V}K{UokZzkalN<-3X2 zW?>P%w1j8|Y(om4=Ivkvb@YAyfC3s}NA;H>AJXe+f<}1lQe^hs1@F|i`odJbzte>1 z`MPC>hXCV<)CD%$QS2YRB^W|u(plvT&*upp_UVq>$<*tscBc23(L1>9X1W>a=R`BA z=07s+^uSCGH`7{t}{R>>m?%MTvz1s^3%M}(vqR` z!ltyReBTdc3Bz@LK`{iwTlW`9PPHX|(0mSG;jU6==%9+nPxp^R!&|O;>yG%DmcNtn zcwKg@(6fEsZ`Gjkua*vLL;`g|SD?9gBDYr7O@D!O?3}jPaN57cXcQ2Uhz!2ipS$3) zrKkHjiC<3G%&o-M!w&M^^{Ls&2>v{s^-NXmxlxcj|9F_b4-Grevdv&Zeelgwvgs(e z82qX~I!{EY>ACJ^p7E}QEhHx6W#-qJOyjolh%En$>0_cz!UA(4K-#Z@3h7C+3k)%aH3&ck<g*Y2hw>cwl&7L& zYejxMNQTPSG9OON1 z0bYZ&ornwQdHFYJ#PF{W>h1GKCF4k$wdl2_sGsvoH8rm3)6sVoMAW3k#evbXS{n87 z6)iUtQ-kplR*>DLnYz<=0xgSZUQepEgN!3TcZS< z!tLoIqoT*_yU0}D>TZyKs)vtO8p?fcJn;VA%1#_UdE(N}ngm^!L*L*m{)n$2F;)=69 zd#sh5Q`sFA4eT)E492yq`3NGLhdCrMRyN@gZCHMO%{xP%p2bl&0LvN!#R@JB;rok2 zPJ+bpvDEMra#b013=RBe>RWrbssHeQ3-9CTTox~pjKleQ$U1ztxo zxM(tUQW&H9d$FXD69;3$I4nd0 zh~kM2Kh#fh>HSj!;s7wTf@1-+*8X3)Vg@?^`CL%EU90OGxJcqO6q;1NyfZl~uYKx@ z!T5V!A46cx5-oaaViCEYL^irFH$e)4WWPwRUOe{SPz3LHA*X5K&sK2as47#2&57Kg#JNHVNURKRsc-CFaNx{ zmSZ@?Vv#;*Ye4tWX!K8e8QGwKq3MU8$wwcqJ7;e_@Ry{~|4k--83~?OpfHSAv$v}P zPUzP0$h&B@u5lSyx)K$&2Diw&fRfZ5c&*K_*Hi;d_GM0`rP8s*gtf>{B4A<8@ViWu zbO3i-=~9ABWPyS1l}_-5M1+BhV6KJhH{D5hIOLAVB+>zk!S2+02yhTvzp)l_j0uwSU}Gc1;gobMINby@h0?{Mv5=;H$F zHqP;Qi{dFyqisc%FNSV(K|uDkUH_;3FTQxy8Fge<^5;w=6yf`r-w$jhWwrj3L5W$p z^oHW_CqP2f*n*@_+Uv7Vp_Uy!RiTEQWGBHd442b!Xf)kJ#`LlcmwLGq^KL^6Qwlt& zP~A7yO7|QgVLJInP;#Fr+&8ueng+|iWIw+qWXARs*N=LtAYUOCtR zq#Qne%+=r*6mu#MU*Z&4%kSa|=fOru{vc5CX~i1@3jDzalJ*nwqD5L*lxh1#e>f!` zM8(>otrf#6f-wPn|7;*lDuXUq-c41==-|*NVt5EueEiQdidr0?cR0aNaUu0NVIc?S zeJ&tqZcDwVj30v_W>ToDb0vPO>z=HlG{ zEFb3rvT9S?4kgdvQPR@t zbbE2OtPgixI2;PW8ryLg^qcV1Rqc%5^rrRtC25;nLTK%0rzIcAcm6{z8)I0n~MAW`$hvbzDy(e4>COi7)l|QW{<* zP>%ibkr`&b~^X)oO=6heQjj~DZ9O+&|_p^Hyh9|WiQ`XQ{*xdNM-`Q zn6ve&LYW8z&Y2Q91Q;i2>uzMUSiKO@Gn$)b|Cv6y+E=*-eMnMBSSLBTO#S7#6Rxp( zca<=scKs}+PvH+1a(LJoQ8ffM8IQx<7^W~YR{}LfiGE}Z>CeeKx45cr>dUb` zrH_ngM^E{$9~|AJOF9S_!b|ev}i@|bM`toZJ{jeUBczfHiGnB4-6oyk4%M^Rr)2fis7$Pg!Xr`15+ zUhQP7i%8S|)MDLY0iDi{wxyqv!UL1+Gq#@|M*2Td-tZ!TV^VV+DfDaRejo~M^W?z7 zIda{P_e(QIo>eSCoKx+3QvA&^={Q1xpRi~}GC5t4h+%}~V8zvHS)9ZM`|;2Cu9;8= zp5^~$um=JE>SwnEn}^te(AZA1nmB|5{@;n{A4t2z`KQZu+#jH28$%`iwCM0sK(;97 zs@+b;O9k|@-LEzGr?F~1U%(kjU2d<@eW17;U-7qS*OgwokP%TB4F?G6`ZLm?WYgmW zUmbVJ0}Xf@eD?DnjQG{wpVx8Oj0V!63gYpjX*iikBE~;edxH0%h&)dh8vHUAUSifZ zMdpbGnMU<5PI-R*0ec9b8=h5{cXf$^hlg=u%^`&rl4&8#a^mJ#8?K+*GjTX!6 zH#$5LtJPZib@8^`1MTZR*H50xP~&|!j+7fzKI847Nwl)Cj#z(C>vFPxl;QAM5SfOS z^X15%!M6J(E*2~)GB`V=L|?7-IIqg)Fq|)rl0;B1clQCsZ>9bD%yqDVN{aaL zLdI#+=fhgm+Y8hbV>{jmas1r*{5rmwBkVb|a~o6uB?9Ui22fW@XXoe7`LeszqOUtN zrgwMHC3(OmE=NDZOGq1DRr(|) zxEZ9eo5IuBWENE2WJ}=i$Hkv1Ati|Of?RBcbtZeQ;XQ}mE233a=iwwxg43GBpUG2f zZO*rQP|TD@mk(VfF0AwOv?xK1&+r_~+vH2n)KpO?mA#UW!Q?jamMJ35ZR7-hOS^;E z`;{Z$`lgf%_(47`_;)hAO)fEqxnBP*#lnIZk>S~8cvDT!4Gl7v=hZ^PLN4s~;CU*& z$(D9Y!g;!UD&_6?QiB#Be}>cc{VK8$%V)e6d}qU%*->QM+yyo|QL{EBJI|Z`%@c0U z*E721=esr&<@V3(2@g~5hG z`h8Azk>{^Vt&=d-rwvi&#`C!YaHxKF>AlkTz6;+ zug@Efu`uRKl^TDg2qUIDD%f7I__@(y#4@AC7yclYG?~K;^F$dEmPBtb$~%RMz(3ut zn3g)f|3nzbb^peeI(-ximo|~KL*8~apV;n3@UmZZTSrR{&bo=c{qws7I+V{{j*a8> z{-r*7#z^Q%1d29c^rhTgVqDNfctq?eZ#POs?p151hkJKSO+2K&YNza`^t87wxFx|s z(M>aqy-g##-^M`6;CdVtc^SvUcgTc@U9CzsosD-wL2g#9`*D(+ImefHab}G?_+h%g z8TmA}`+G9oVB&fvS^y$agh{f;z+bgN%x+?B`I^>imGPjQ^l$mNvywpj`(M;%5U73& zRUOjr)%a4D@TVO;)S-t++XtP3ft6KpA34$TTcL_l*AZ5c{SaY`L1bq{TF9<)yHFjsN za9c`l30PkYt8ZMb1*o6->vY*h^R{=-B)0fjRbJkdHo{hm?-o03J)|HI>2O(EID(0a zz7MA@yy!}}i@j)eb=@RYgES*EO4HkGL}t!$-~s77LD#NI_Qrs}pLExEo!oef$g6%U zoEWp*qBb|}B6FncNRBY?fD~q$ufL3CU^kF!*PN}|-=HEfGIRGA+>SROg5$uDP42R7 z{MYW2|JeP!B7A`FG#SU>Iw@5_kpM%l|!BhXQO6(%OczekIX1f6Z_uIW8 z|5*d9|44oa!t(R;kB&^bbZh3!V8KED=C1Po2&Ms=f@R9%#`SS=ag~)ccvKxt7A zz{5{VcNfmLTukuj@_3>C7mZRb`iTMxw%2YYp!#2pzc;^%&%>!h|FtO<|wb)l%(pT61-*T+~S6vna z85Ydmb9obHpg}%^`gwQ$Vi|vrMf7S}#*}i8#l~e4jP$}vbdx9x%uji*N4(Ypl@}0|_nvA8nSOCn)m1(WdF! z=5ec{!ehq>0OWsJ>BdtKz^8zWkB*LpxqEyL<9u~?Rb5q4Q(9SQJ}eAbt=M$SUB7mN zIIHK-oO!Xk$l4SwBLg1{3j-G&9z(_9yk=0|Fq&aHQ!m-BuI*6PI!HEOp+z`j+xa{`oGf+9!Y-mus|$E{{8i~4Cw3Y z24Sg$Kbxa$@$}^-hwg@2$vW(lIH^vfKKJCv8()0mCM->;TL?oQWR5IxZr1rQskpNj zQ=LC`(NIL)%)7woSCFuR&Zv7)rPa%9-X$(Bcwk?MDph=8Nl`WxO=GyLr)OCq>Rsul z7jgeU+oXA*5&;9?au@^!DbyF=tX9GF<`&2IT@PKQJ0$SnOrtGsp<9vPD2m*H$wDYo znvxh&YVrZcKsp+aQpuoAS3~Kz+kyqRDyf`QxoSz^{S1z%p;bkYp{E923P452HJ^S7 z&Kbdm9x0M_Rrf#w5w%Td9w>*Ny-*hmmp~F)2p^)6{~(o4en59eLN@qXt_(YXn%%KO zl;N%T7S|}?+vLqN$imi=l9X?5+M7xhJF81HSp{OkU?*f62=joo-h5qu?71IxI<1WN zl%Bm@iHgn#K#+Y%UA+!ZC52(yoNR#rLEpO<{nA^T?bA$?3!Y+aT6%G94dw+#^s1 zEQ!<^-uE!wyN+Sagu$nC6qhGc$lL{P>=GrsC!KeF$r?UXSd&v)rQvc{ zx|)p^^Ph6PKM*k>!brAeZhhTzSJB>*+T$k-G;{#FqP$BKUb5rO4btyoh+ckj^3W10 z3cL3CHj4 zb@jJjGm50Km7jZ@8HGR_@DtLuHr61PbTj5U6Osu*obk%0;YAMfvm&l|Md`X~Rr~I3 z+Oi33Bd?=LNsvnec6~P_Nn|LC)E0_i7~@(tJ&PaZkP+dCk4eAN6$3gg2H?}B$x^9@ zqsCW#Zx{&JSg3JXk`E7)FD&-U30N@V?4H_{TcnE=Svc`IZQen#xVb`6b*$NML- z=fIKti`7u9-^hHc;+CvehDI0Rx)iv)eki!kIh_I)H9^+AG|P$y691MH)8p@9A(lro zD;|RnQ<7832S@xZh$-#m-9^DjHtjy0$)TaCD$3ie^706=yI-Uv_GrA|G;T$Ewc@qu z);k|Pt5jj)p@WI&U0Eo&;qf`gk8i1FS67Q9uyZ&#?Cq>5q(u|ad0~6B)6!fT`MjQWB429P{VX}X zy+n-2FkOAtMHOlz&C=aJfVoV!3Afw*4Sjr%f^2RFG5=Y6dV3MWBBR03#N=$RcMC?~ zJa|VyfBdPs#NBrgA9zh_T}iu(kg`pOdBhTcX{m0)ZZ3yY&!Rf;zRs#8$~qJ7+i%g& zU4!ceM&SQoz}IbaAjN-T*vVjHq%vxZ)!uO0H8Z-U%vBsMqACIgzOh+T)myMzQ}x^< zG|jM@Q-~)N^nWUkE)Bbx_=U!n&f6BeSsS;bA0Jj;SuIH2RnZ|ursMq+<6!HQ|GL2;?JAGW4ZV6A9qUjVriujAnU*FRM}Rw&ELLg zHSUwUVQX;}bqW!};>e;BWPK$(Kd41_o4b|C32Re<7WgHC9BVUL@)rX5`1m??L2UYN zOoyEpp7D7GH2|Cjf%!Fr)Dy5mi26VKPgz$mkWq17#bal>%*jwkrkE?M{{+jY6jrS_ z-=`}w+e(H(Gv-5uzkIwF!F)dxw$(ylaWF&(gJ=mjgY9NA{Jyz)nB#*U8^T|DU|Y1i zB>?#dWat!B>=yjOOkAI7pSNjk(hQ3JOe-zj$rZ>*#c%@)503y;Sn1-`0QbUG=k@~X z(x@s*9p1aoHK+e>#YacG!VPO{>kl4IG+>okyFZ8wJjaFmj17gT$XdF;nNq)p`FVAG zsC;K{>Nd)gpOb*r;95o$Unafgaxq`mhZEGUBS4BmSdpowy45tVt-rV`nD`|MZwG$m zK{ZyRLlWvQIk|I7R6|jlI@~QHg_C}@wSbYs0e$9%Yt7%AgTM(q@4q34hEtW7^V$#W z3&}b*iq;VRh#Uke9G$VUab0AAFZ^+$MiC#zjXG>X{D~@}h;ea~;YAV^C4x1N5pkk` zSj7u#kULma8}0jOX)Sb+!^BJ9{Wf?7Bcm&ImCMAhg}NfDq!^!`uR^G}%F*62ZpI@X z*2x|l=pK%W6o|YhSVLF|>y)9uN!;5xVa0at>@jos%1Kr?ck&9_Y_c(mE6{Kos7H}Z zO2Yu_6lZ}?O6oNn=2d`(oGwj+6zJcjPUH_Ap)Zvz1y>cM3OWJ}PEg=2d1a&xOPA7m zeaZE1AJsNpg!MbtzqnpGaE+yJsq3Z$(3kM`a2dkAR3>w-?*u~{YfsLs9VUM6_ zvY5*YX}AYNN#|tgRUd+K&EjgaJ6k-}=84DV_^l%igi<9-)m%8sn21$eC+3Npb~|uw zo0;r|#PB+i^&HMK>RZ+{*wH!kB(4_DXclCx_7>1&}T}|tB10;KTN)A zGJ-!0+N9!AD3mSf)de|a9RwvM4kU}nXu;}a+NgEszjFPF%tQ(H?IB}=BuFixSdPD-Zl4s{PFinJ$^&ey-;yd|Pd zINF_es*A_r2|R#LhC@e9L)G5Vv5Vfkx0^J+!dUR&@maK|h=9Nn91LpN$i=bt0|OBN zE7+uxj6t;y$s1<3sr3D9g?}@a5f~G||`A0Pxb-J>q(^IEZ5ipF19ifKZU7;1o`%Ot zN5mjSm)f6>D!=&l())_xLXgW`%LnA+uK*-zO7+}i`JOHF1e?`#TC@j~X)%@!YDgl8 z)|*wggZ^)FePSWUo+qS3hxt1 zL<+~$q%XG%q}BUDLHSEUXo3{TId(^70qgRt28MGcbCw}WN@O0|*;cT7=EWR2IM#f` zgv-^!_EgkdrtJ?bwh^1NoeHY#f9MT=e`0`b2?+U9+PuF7NYc@aRjErKP)a4sW()fv zQ3=DTMJ1cp07Rc~`=bLt1d153j;dYmlen>~ruUhE*%7Epe{Br2w@GRLyiTZrdprg{ zgzb%49wBmHzLT3O_%c8nwv>~rou?^WAX~qHNWTHh%pWlVToutz>jpj{334r3%9dRr z_|Db2&(+CdcY=%cB9w{%u>A+f)V!akdcXv$Irps|BdT~qG}gaX-vlXxHAKn!fz%Um z?=vJo=wLS9Xn!OIDBqMVsng99wPdq(+_}4~0O;w*J7}H7HaLSsHDLvx`wppGG3=;T zVN8TUW7Z+}GRgKE22EGKjqjFzBdn1??qddeFv)bk#;Lg%@&jgdj2$A>gJJ3SUoDl2 zs(*3d%)WtNO8l8Yk~I8#L1FP^sVs72A(2W_TDDl@6^k3+`ihZH^;+K=@)@=S5@DWU zx){+ZYM)k99HM~#5h@sh>`x`NEMZS4VMP=)k~vzTz#->ovx2saD}C|gj7%JSC8N_6 zI9k+Jz8jfLc1!hL*KOS^lfkHBDGu;VR7r4H&yz1qm5wDV&N+%nCOTUhuWAX5fnd38 zF7MF>M}tZ+5EC$P763~WuWBD5CgJ=rrY8(${0N!7{u%^|td$ikIe>JmS}fp%-L9tIJR9P0RG=wHhataZF*?lE;>T&dHRHoIJ#E%~UXbi;PvOtd?Fr>4g=vU+AE^}?V>Xd&;u!R>dZiNx4+!!U1+B;E7*MylMJp zyJ1ptvFMI#O$ku<|0W4~Btf}4xOSsaKSI_#odMK9rS)o-DtJUUm4r?$yK8gc@|^P4 zCyzEiU_E%v+BXk&!S;Z6SpJX=S7?PR{sLW>vF>SUY0v+d-Xq4zX{HV)%w2<&^Fxbm zm`GnF>dc0Xh`fJPvHuNe^9!saSKDtvrZ+?8=g&yy>3%ogCOvJ~%414`12e*8y(&Q@ zeE@JM@pxv3f<0MDZIa_LeE^}y+KzD;=mdT%T5&s^@H^B!&iW0TE4TSA6GthLG?u{2 zn|Wzf+W?J6mn3}1!pUr01^VAF@QxrP9UY4#t|nlNmRvl!CH&AXzWT5m>hdL!QK2R* zVlvb!Gsh?uU&=-{PqBZPTv=DF*A4$!FJqrKj8LW`ai~D6?K?Sr#%c8#4IgOv8@p!1ixoVF7~+htY75k8%{nUC8T#SP?-(K|gMe z4PRM@`4ud~Gi0mHSnfkV{jzmM!?fq?tGKe%FN-sM!n;&DG<@)`5Mq{~)mDZRCV>v1 z%ZXn4sopBlJ|x+3kdtX@@Hb<8?WY4TX)UeLaR`GUoy>bt$?BJjxyYSf4hjmy4ZGgs zg38mY@23Ti{t{^QA~9?4K+o|n&X?8L*x~{aW2<90_k1bk=! zGxGbWXW}rFl1m^%z0>aN2? zHV`aLqTjCz{M>S&07_Ke&+;fB%F6DPoG2aGiSca>rvAReS}AKA8x!lp^5)d;yvkyr zDf4syZUjM*VbLYOMVMc{mTg(fYIZ*4R1T-v8{&wx@4wvs4YdF7H7toYiksn z-2qI%0v$!~5BzW;j z78hCJWTt%I_L0qg$MWeK<A+U-X*R#P!qU-9O;IM{yokVql=sNg}opk+h?Lj)(x$O-iPuK)cPKsYCX`ns~M zhLu{gfJSL+ZCLh^82(!MCIC9YoY%40)lE!@D!B$b8D~kwp_7dPSGV8XNar^G6CeHM zU|NTTllE~mPYF4Ep5H+v!hYmPrU~vBEZQU@Sr?(%QI>0A#ZNvRR{?&vvrZ^X@n)~i z8s00p$dc*G&d#nTekf`}y}Eqi6i!clQ%`SXau zU{jnYqC$wxE?T?AVlb-pyC@x3Tg$=7ltL)X2ZoEEreo4ZDWYYy)Q;P1^7Zlv%Elbe5lCQ_%f3nHnZE3yy=w(YnA18Aux2vJMui0?q_HjD%}sx7`dIpIo!E4l zZa>zNKTr+t?jYL<5RdB5_R)JW2q9i8%;Gx2ptwm1L%ua6T!mkjoz7BVhx1L1(uWp>%y!j0%#h0oOozds`odVklx!8I#;0pl^BLv01S0Ic8Ws z?$f1;hUb%Aai|kqS(k2g{6B1cV~}7=vu)d&#6?r=SLntqmO18+^dJ868WYe?9B!M>KkC5Vy>$n%wzUxD2H+dUyfk2!S)qKs z_v#A(0WrjJn%Rkpc0^JM_8fP3SI+joU6|b%oWnoj%;$Fa;eQ!UVToY9BS-8)60i41bTQ zN{5G)rub|cDW#8>!2p6Ar};&xz!mW7A1`H}{g5dbWosB2NBvW zQny|$kewaqW>t7V)YS}P?S6is>|*r4Nl^VTaL%Npq`k4gT-81u_J1v9tZ!jEB(c!0 zSUvdH{bkLiMRzt#2P*1d82k*O^C3@RGL`3fa5U`9h7j~=DL8z8HDu`_fKXzjVCT%Q zDYi!CYNkeSdcPa)805RR^LsItM&=Aa$uCIPyutQ+w%C!)40J)$~FvbP^;)Jb4)NuhhlrjP!^j%b<(n?>E`PIaGaG9IGCmm>6+uI0_q0`+3d=i#Ljp zFt<4o1uC_(cj%)<9cjq7VBek-W862{$m8(#*#43jQQ6)o2kr4p8?&&A3GKmqduqS7 z&GeNN6Qgqje>qyOoC-!!ZL8NUSOAFIA%crEHEQqm%l~V`W^EsaG>@K1j58FvraS3A zdRQ;9yAqf3YnynURWZ3aSy1&jz5|v|6{NL5L0n47$j{f-&@m!JCnU%8AraIEN<_j) zf5#~)=OcEM14%4av0#k>Nqe`byY?LGq`@!PPapSO*T8JE%GIrQZH*HKY@Bq4@79!w zfU?P7yHa`#Ss^`c-iv9w7&cffz{X8wK8I;P=AEj223W8BCQh>Ogc+dY8_rKlEDuW@ ztf1#D$&A#KnUw3SlZ*X=#!laH3L7B@iX{il93jwAG$v5@KurQ=;t;*ZR>(}wbn#YF z*i=wZV5WJc(no|#AN0wDbo|LBc&!WA$t(@V!%k9slz8OeU`=PQh_drtg)obR&~VOE z)6ZvM(ntt{JT5?E;AK`pPjLHz$$K;ZMhagNPE3;esA^f_M>0c|fmR6XaXZxQ_LH*c ziSlQ@C`yEz3zY4=UjL%l%WpTe&nG0VlcH~U*!Bs#Kn*H{Mm-2GO*EzBd@#*s^cYuS zZQVyVWUb>5+qhZoqe7pM_QasnzHv2a zgh<~_mY^Xeh1;_-V27LG(gR;#-JD-opkgYs@5TMQP%|s^cZapBw6L(g96Fk&KOH1t z{1i`by1pW>?@}Gd)WKvhW?TlHP*p4!k5`vBzRL3ILLFg=Ng|$bukzN=Y)wsR_h@i1 z)n-t=<6NU%3>2+3pX_E>n8j2Xtjv;GEC;`ifk6iKcOSSh9)RaXJW1rf(4AKYd!pj@ zdW)s@NI?*ya{7((Mm{4pfHZD3Mry)XVC#yMdJY4So! zpzCyC_4YgMUqCrdA-**#A*C>H__`VUelt-esVCeffwba6H1atX0*7ct&eg`f*C`tq z<+jYD{b&TCBo^RR$TbY@V7}Z~TKlF--z}^vwtCUEQ(5HUD&QwWQr2sa&XLfU!Inp` zi$0CYm-4s4@OjBAoau#-gp}Fd=^%>1pp+*BbR5!M5_QBG*H8D;_CstlsFaw@8SHtxF zJeq4pnz)HfD`rf6Ba4{YQJ~A#7KB8;l*|ufmQ#G^c-P}5#iU~y^vRiPdE@u-#gWX; ztN}WX6A5dEn!3BGf&dLx`6SFo*MAs2WN1w%E+HpGoYLeq8w>&7?ZMK(CV6iz3MP#k zavdjf^oeR6r*&p5GuVTg>UJ=axaWdge%Rep^*Z$CH1V5k6EMJ8q*QN;_#2Ddhr(no zlCLL5^;6;)5&{*3|DDoOh2Tew(vks88(VV-=6Mr~!t$uk`5Oj;tuwfJ4OTUXT^xyu zWd*A$IR^_#_HG6F`SO`}Wx&l@VsJ%g8E`Es(CV8Wx(7NhMq%q(E|Nkhl?kfgtZi=$ zVJT>lfC^c%I>Db*{-y=c_NL}<123E&VRye~s}`6GAhYwMK&T(4k&U%wby=x>`LbWe z)BzS&Jh7q}3$7Np`;pX3K&k52Vi7GKm*oLBgR!oc#)e&U87v(EfWZ5>dWW|RyrG`3 zZ2D|F2#8iJTRB6lRyjW+A{I>gdSP;Pk9ls`{;+@X`_mJgo#*wUnjtvsxfq(<<&d$Tcn@`o@$zjFT*!byw za2QypO&9NlCe$*1^{5YJP%3Vnkfv3M2X9 z5T_Ts2nCXJ%S81Q`%(XnQ_Jy9IkVEIN(R~AipqiCFf8`o3IgLGJ&_% z@GG*F{S{jN_+=4925ItCr}-Sev_eA~Oy=IDrLr3?)NN_MPb+@O$1z}_3*;O+;zH_D z;HsO6qM&8-h%L5y-=lIbcrU|3D#h>Iisc(ageie4ae?de<`cU@`&`i+{8{4W8G0N|;!tHY$P2bUOgYiaFqI%GLmo zv?${Afbm9ULQwu@@r(N-{^{8_eOksn{k?^! zzhygHrHX1+Xi_5~qHv#sTV^czz)UBi`0edVVvHt0L3uo+?X3{KB+<=7Eyjq0hWlR~ zb+S~Y|FQ^UC6OhH_b!MHV4U;wd4C3gb@nc}4Z7J^A6MWNGp%1FUl7WZi5-~5`}yHs zFw=DgE1oRHLsww8T0I1Y;Nx+$AR3@84G%PoBH^1ncL7hxUPH_y$;Y`7of5*yK%#?w zqB|`~YcJA3WHy@nA}TbV7^nEo0^pBkeN&O<&n4_%^N~vAKm<&^9M*r{YhGnAn3}nN z6??qMIB>18o}VJiW+_7gI^$v3Tv9`9ra43uMg}Es;ucR_I)w9l@rGjh=>V6&=z&e7 ztWO+WD!E>iOdu8e298{0%oamM^WGNU``!GM*f{>L4g}g>(*Ijwyn(; z+Fu<4LcAeI&$nqv_ywX*YtWQ@DMFRehauzVsPZLD16dD&e$oGjIoOnQx==r?$ zGMF28nf{-5`#*g=y`6>y9GUxv@mkSbZ=Q8R&E#x8yW4E8z;+_Jd-uoI3Y%LlnVXc5 z;kS@50jwr7HZv97@FGW&AjEG(ha-t&Q+EQ2nwgqBQw(^QBI@n}T2_ADYnOAg=typx z)phszfE#?(fZK9TB8U1Np#+m87+T>ynA9LU>(18JC-aCsx@a*nXYK77uZp{SyHZLP z5_Oz7IE9ROSnY5jexe}J4BGMhe^XQC)zwE+xdUK*J>FtF-=Cl*+uv@%vN+e^yi0Ms z>lvV|U?KALca(oafdSO50YY8)QIuO|R@Md)Z3RCjI`!iSzA(K=;la=_IAsVaf<>r4 zq*?ny8uqe-d_+JcrB0H_IV?1$BU1p2E6DG}Om#Iy{DT$x?Og+DT31H8xf z%=ZZiD4`(ZK+HU9kslc7#W{Dq7|3W`W%__8io>ys~Fl(yH52cMPT>n}~{ z*CW8^o%G~NtejX@laX7a^XhNi3;yNx@Rh7wj3OJqPW$W$wc)irVY4W5{*5jSNWxH1 z+^jv#L~gVbw)h%`Xtx2JNzKNF))g`*o6eVKJC-M{`{#s=&U^~a%M@`%pnBh@-O~dT zBfY1za(%46Ppzy!WwePqxQScnyt`$L&#a&Jk#9^UV|2bq4sl2yeOIa|Hum*#WxbJZZ5~rE4@E%YF01M z&XR$=F}tD(R7d!@DrGdcJgRi-z*#*qeMVwo)&d)%OxB#!_S6%Z;hpZ5snSy5YUI*J985EWT^o!WQ~v}EXQRzMJX?TXovC{dq7qf^hf;#X>C5A)#~IYT6e)Z}6Bl zXmvoB3)2AW!Q#>Y^5)b~pQa_&FKCE%y0(Gs-&Casl zgBcaVKo6#zl=O$(zvheJ)HzU+nt^e~cuU8moTkSz`(4+!mC$AY0a)TSkUZ=-P!@B; zONX_Jn*bDoU5E)#VCC6+KpFvjx;YFb@FK%Jt3QKB5 zDO)IMc_dMKfxwU_N^%>WV@ry`SwO7klxzj_-hOgXq{#RRwPOG@_^_V9ZwL1bO3Rj^ z549QIc4BKKu8vPzk2`NC@5}zyTh5DJK8T3~4V~?2l)_TtUWvOxR?c1yp6+3aubK3! z8DBEXvv`>;AkjJ&A~ZP|5<+QkcJSihB1pHZe@B@5M{OZ&l`!!Vk0#*ERNSQ~#PhBI zVJpkKK;klbvcCV^c)gwF;W~bgf+Dx z_%>h%E@E4PepqEXKgS?ItL>4FV+MX+#=q~Pf~jPCa`$r3)CyN2kw1&l&;u)-xk8jk zO35|1zN#p-^GF+ob+n{k3zQ!-hYue>RMcXrJ)^N~5cR$8viy}?onMbsgbG0zBB-MS zcNiIl`ufrrrl(LvboCuksWCB}?7l;rM2+K|Lr$f&u(C5qbXw@g#`#~;Z#)paMo?Nz zJ#e|*4@n5s7^Us}g#E)^jfMU{LC11q-EQ(?#8QYuoG|Hx!Q^03fuA}JEqdFV?DfA= z1M$|&Ii@mL8W%Z;kVSszU*scyx)kk{l}_YzH(8tAhd^(Z>IV$IE2*h@Rk%LO(VP+d z*y5A1R7T@hE%m9rOX6L_o@UYup&(fuE4nA{sX|0G=bmyxMUPUn4y(IVZpfQ#po6=>J4*{&N?|a6AsjuO6TR!=(Xz(G+xE>gDT?ar#+np%h!^N@l?U?TM3AUK zgRA06hHRXxPZvbsgmXZL-kLJcoO0K8gibCOGibOlQP2Um)LPK$v&_bpEotxwX{%}U z-+}Pi-2KsRaG)qO(utO>W)1!z4hrRE9^b6wL>z~&mp^_XITI*Ai9D_SE;_8)TG&ms z1Iu%zB@ag$-6KK--4%J>QK(R*VQP_b%|0=(+hjB3-~xKO^om6B64c^-9heh(#?ymS z4R9<^fltiaI;?8MvJ*^|^9t2{-d~g0zmVdvb37NKe!~UkI}ZDk5^lNl51J#7T*yte z5YY|PyfBD71&A5acI-r~d&kv^7Y%_`D^m!#utolN{Q^9@uRY@CRUGZFz;!MfhS+@) z8JD=oc%OKP0bN(t3{GKP+Fuo(e5-(@!9ANIgHw%J%WFXj0q+PE#}z(p8Lk^Xmin`+ z_dv-VtNkIptVygNvR%Jl=NHd?r`hRwnpz&hRSzu+vGG?Glym9~Oq)wH?b;=t!(VB+ z?spCbC{dyl8G9K^qxTz12XEK9k}Y6yXaHx*T%OZ7#B_O8vm2Yh@r{C!z2ed`vB>MD zr-yrfH}~qDoLxb}0E`2HSp^9Q&X|&?rD?u!0mI2DNMz{2h$*6ZrcBL?D3|C%KiX70l0viNXn5rf$SC}xx}A$Om#f;{xGJaO*7FxHEb)ZGnB@lm@dWfl8qA}S%rj~gO1faHMdEb1kQyUWyqb8{Ioce} z^z2T5-;zE$n(}lsRk$+RV_FYG8nzEJ(rog$Mivh%SQ&u&4FJNgl$Yta#RC8VgoiOm zprCO)4}MiYy=GbcnGFkJs(;y-T!NKG8}9dfh?{TfK4-k!@tmFYb*Iw}h6 zzO#NRzox7ff6wN*vlYC*f$tXt03aR!$P$2m$VlTaMhkn`jX!2cOA(o~F0{K|oiOMZ zLagZStTWL9rQ6mInTxwM`453!fVxzO`E*wc>s6Mc;dO{0;hd->};f0R!IHTlEyxF=w}`|z##$8s<9>x*y!H4#pJl>gYO00LBt zt%Hiqh5m`!f4ulV0|5NER0zQ(B#%iMgzx|a_&+vzjsa+4xx)24LH|$a|9>ND41iM! zK6^L`3rkDU(NWuif@VPf(?~zV`r839&XXLNws^n0xEY@rM@z&fwmI=J?7-T$b4OUW zR2@n-1pU;LF2M2?-YDMplKlt703H*@<1jM70_0vZy zo~+>XdmrG*8x6l2^zw&I79fUu&#o2}#W=6~5sPKm~gKn?NZ^Q9d}oB`NuiQ&_Ru z-6*j?)0o-1P|Hmi_S`p+VAC|HI3N5(HWk*6H^!4GUHE-3w{SS zNly#)bXSL375Nn^K|`japl{mW5jL%dt~IVU6qUGqdgT93DOe*~B<}QCEPziRBF@kbs$e zrcb|i79dvlN?6CBob``%?n#dgO4dfz+=xU*A7*A+Y$XZ|h_dTT+>cj_Xy`Yfej|vA>s;HNPmyK~TPq8nqW>%qj?pK5P4u%Xu8piI@$6r0`Us*6O zcMYS^ffn8m8jhWk?b(4hfb7`qi!*_q#7~;J>^yU3bpY#}}Kz zdfUvA{HGalhRs`G=+xCzxly5BZkSPznLf=U1vme_d!?ckunuM{M=fdD$Qo zuxk3gNH1Q0_>uIDJ$2TVli)f=;k>_|17i5UrBuVu(Y+pWW^dGX*5rU}LWgV~&`n3W z=(63N4$kz{pTl#lbttA+4xqh@iI#^2nAqkS&ga@Lp%gkotPisKIB=@NfogSLwjQS! zrof|;#m-H%W$MN)<^qMpJZ8W5pJEF7XPLZlsHVThtq030A7PnrxIaqF1EsF}!rmev0h*1HN)AVuKlLk3stR+b4uY*dkzT>Ave@fwhpM1wlHss0rNB&)f- zL-CObr!8ns_7(N>Rr95G`MkX=Nnl*Oz0NjJwsEpv=8EwpYP%T^0$pYp2NyRR~u zol$*}NgRC)^^9KHJ-g`BiHfb(fOFl<`-7o|;!mh*r+Ob)Cwg<#G^s0H>qNX_NDsF8 z$(L}3S79_<<_s1PZBjDDQ{C$XC5e_22D)%A6i%pJl=a!Lm8(Sd{G;!r%lEMeSsTM? z^!I@jcIh}(xvJ5jNWf*VW~1-ruMc)u3?M_+{1d`jg?4*g{j4L`?}vRJTx!W_iivSS zX=@RrV9E%VEAl?N{MiB(%Jj77t=1K01dR~7ZG9nf6xi26SWVXYm}-^)o4&Epu`%Rg z%Wx>=>#rAG&JWo>C}$fPm)}X+p+=GW`5zS>k$FRc+e-A`Z@f0qle|FUKlMD+{qcQN z^gVPw{i{ELZ|ASIZ`*kh;p3MwXQi*Lswl_3Zx2gneMMrnJJJMYxoZW(4Lgrzh#xKS z%5hTk6*`KNyPh0y##n<*##`8>lak|#7mTKxvRjZJT$s6@=OW%`6`8D;&#n7D)<9#L1miY`?_zK9VX4o!i;sO`LEfXvjFiC#z?t3kXhQVod z-cxS7wTkt2-2&a?w;M@DvxweATx(zG9}DjNZ%ig(%VrRk!k(sWOk7k*eZTivr3^4I z45AxPPW#R>y~qMCh=U?_(nGRTG8!&pv}(JJt}?#)*-g(jiTU=&_=a z?CcJnR%F{)d`cGPD9yfAS7c>H-xs^>g6bL$W2+~gvIMh(SYX2yv`&M95j zX#(6{-792ONjJ~2xhb7md`gpwg+=`1%3rP#VV@Y1;gAQ$d@_M;T3d^pWIyGEAx40i zFBlgCXYZd-Jey;p@K)r9s*#0E1?cA{j#1O;c+ z<9^*$_RO32BUK~Yn*w!Sth6G%GM}7s;&y!r9+1c_;dtzh%a$fEYx2~WUf(Du&;y3a z?=S0oC+k|hjJdiho20fyf1EsCC4cQVk0YN=0D0-*8ChKjP+*vl1g}-gJZ-pjTgF~S zPi(%>?^Ta`tc@$d8~A~KTP%)Ys|)9CU@OBgyrcu-siLW-k*96M|M8W zvwk}PL0Y}fvFcFEA1v|SrBVWm#`5jy6ZkXQcG4t&N_*?C&g**V-!w*>^7;b2GCwrO zbU3^5Xy|w4gBF6Dq!g~7$LR*tI(IyBFEs~&X{dGUreCG$N1(0s8;n0upJ?l2wth^> z`a~7Y7ny$d`(H9cyWz>>THL*;ey=x;QAN(`y1k>49s$u9K!hmI=97MXUd!Z!Z*1y* zuk}j5Y2tpYW8#Krn?|(5Ux@m^uDhL&KKm}Qh|wEund*2uOB!z_9Uap=ec(V6;RsV` zQ+dl2ahT^1{dP&KgY8A2K=yh_tnQ2@rJ*UOY{$MG3ZsmD@ve1}D>dUI#9_N%bDi!G zi7%w}dS6eZKxX-6PsD;4HkBLAIWcgLpU21s_|I0%)d0fgrJ+19QjeaCxS!{If_!AZ zLqo~KC?f@H&?U!oD+mn%2KRH^<3B-wYBX3Mu2eI;ZmY`ILegC11|qr7ByA8n zY`$5MJNL!RP{^Cz1d5m+!XpSGLiB5zT0rT3{xc_?vHQ>2iUKFZh0|Ll0#;Ehh{3pp z<$Q#oMyV@Z_LPu+HD%NSj&O^3$+puv_A#)Wi=TZG`c;9u?B#$L^}#hmv-W& z+!obsGwIuYQO>bQ+A9i*fwXYBLW)WSyHQ(L%!Yk?V+L8{cBXm^tx#GG5W8f~x9d}w zjL+GICFVbB&Mb{TuUc=rTl}AYUB@DA1xtqAfR^iUv#+F4pw)tOOV+xl0A zIyEU0g6~PeDbp+|p{Fu%rM~Rip^mCnX|SqZtH(YSd4Q!$k6D=TjSrg7gqX*2aPgTy z@M%xh5dL1|FAfwmGv|T>9wUxa_p&@Q5XRY0h_~U7L9L=tF&8P$!KZ zIPQ|O`OkUj(Wv(5PYdh#iT4{xA8j1UjlTp9V6+TQPUCu2FtPT<`6obv{Gz9lIsf3u z+3n?LR3t5|xe<*eA6Gijw>r5BO1Z?_k)O*`qnJ$?V2zt9ZZiN8?l;gZq3 z$xXg-j*5{@D$E}~4Glm*1tEw%QGZwa{#d6IILVR`OBM60y`J0{3TTb`MPXMfk``hJ zthOgF)DY%ar{&0SS`anQtC^VxyS0NEZ;cqKs~2%E%H@x|jd~w$kf^}KpfaCurohUu zMJLZnJ{7WSf6W^+{a&P42Os$FeeXO2U=np6<-G;5P=kbOTHzoMWs{BnJ$nlhs~Ang zwC-6dC8~p@x-9gBN4zDTx6Rb-+@d_(G4U&?aH3+;;MBI;#nSBShh?4!9Q*V326Qd7 z{g9ATVxMnPL5;Eu7CKpob`wzfb_i`tTPhWVWgr2*I?~)|4yT_PWDJIevYJMvkg}dBBYHmjNosQ+wbjjI zZ;`fvsEkB=y`_8#HyxB|9Tie(J<9%_KW6Sw&z}a#DHu4Nm z<`~jUV&Du>ewehED)1ilw{G~)%8NtmQL(^eMQt@U+lbBWZp^2xOM{TZgb?wSqSiqV zZv_2uzi9r0%$Ae!wpDq~!$*4I*AHxTpsMd#*r-&@l^vF#RbJ+O$J<&h{NqCFy>G8A zj$ul5)KZVa6w1x|&-yMuT%w$eo)nvJj|>-bLZlmEv?;e#zh#0F z61va4b*UF^9D7fRT>&Xrv#9>BX|Gx))Z(I$b&j=1l8EJiLtFQ@F1azt&B&a)R?q&d zp#fAHI$OE3{t4x=I9*`uK(Ueu1;ZjwcgE^-{l8h`-5;peU)s{E>Z@fJQqE0knc7-9 zN#9G)UnuUwGecEAS6n)Hy^-0<6T{|C#vTpJ={iNDW^Qkk+3&N{-zUl1U8oHlrz;^G zr2V(;>_8rn6N|h9Q|klyVip78lcOo|EKTgC>pe$j;%);-VxQi>WWCQshUQ}%c$^9M zVUe6`2VP?r#QuxvQ3Q(Q>r>*zPZ%k4P)=lrs*b5^?J$#W1{$Q>PZ`}MfYc(C9G6Dj zMrU5Hd1|y}BRXs*Ap&W=)Pe5#dZy(5c9Eqf^ z8Q3`Q!Rk`_#?(%rJCiGsqbS+so4#EO#zZn9Ov)V{IGr`T&R!xEv^Q=kv#Y&vP)K!S z38V~Dk2d|mSbjrX*@tyhqZM)d+S_xgY*DTy^*&ZAiR$2f?dgGnTU&%>pP*B&Sw!C9=B+WhU4vx+!-Mk!>xGvU zydkdJ@w{fn#^oW@mc8c*xraK3H$b9YKTglxfe;u!14z)>j7E$B%AX0Z4HHg=|ee5sm9rH0?=xEv;0$W+ZoMpC84-B@#RWnmnApDmsVoF6xwc|w$!6q84wbMakTL5 zkH|-N&=YDMs8^58Lg_);G4XM#XkB)MOusEOco~C?OC1wi-B(uRa&$=e{jt#YT{zLH zyjZbta7;ACg1Y_wVq?9%EsrN^;U=42vOkS)dtQWo&$Z%UA-GlFpDIln0g+KT#EOx2D2bw^&fS`aw2GW%q_-^sr(kP*C#+>3F zUvP9adE5s}k>}_II~kIN9ey_e=m)a!1b{Bs67KbURF(@SM`S`KAt6t`SBHfWPYI?u zzjRMn;V>?7Xr&R9!bF07KHKSgTE^2y*Kvn6Q~MBFl@oq3AcCDf7xD$E9dGc#PJ&x z_(qZ7Bj1|Fui(~j`Nxqzp~?8AVemS8Es$3FSk;qs_|zW$vl5an#~?*Io;-+}<`dEy zEatBuR5l?Olgjvd$qY|jcCC+c0))8(xmL?~YLmYr&5DBicjVs2+#EA^BO7wRhAeY9 zI(qHzlbr)HF5M5=f`f}Ho>BSGMs)p&N8A05V#>XZP-%C7Ric>c#kz{4M>dS){X5Qs zrislYk-=YwSy~6q{!H9PmEBD4_%({I-DX)h1C#^<_&NZl|2(PpfV~Fo5)sL@f&2Tw z@2?zPis{_5NenhIVC8I)cdtMv_W|51;GQ%8>S?=T7Z$9kq`1a<;5Ch~EydxIe6)aNJ)+=ipF8j-7pP+;5D!L&}=)FTC;4@OtuGT~+po3G8#u&A}!%(hH zCBlDPE{Ol&W?WhsAbsa7$Xzt}7MaX?G75QT3jdCNWw!ayC&87|>)pyUFis@5fJ<)iEtMJE;T#k>Xn$YEaZUDKq8P_`UESV($=ZVC>nY%ey>GLhMRr&r!t0_7MK!|0bLEoD z*t$Io`hV$-!5fLo&wp8M4GcFfF_vNC8XE3ps0=EVMLZ>;c4xK*&`wz0Do(hpSp;d*fBfXKSR z$mNv}$QTd%%!^4MBwd`Nsp#hJq|B?gaC7(>amL~c5fV*{^KJE&REXJszvIg*XCxC2 z6oYgATxbfyUDV{RJQ}DB)(zW@;4%f3)GB(bu|HFEyE?1bA}%9pH#--7axZBp{-%v_ zYqB2Z881f~3x#yqhWJS7g93w>lCRf0m+P7|RbP%3Y7#oC_I>@GyL{)>Op8JcoP=|3 z$mn!OMX&h`(>8ECzNNfC44#{eu~u4>@p3T6kQ9AXxX+ZWt|>k5k0}^Zvi3Dlq+cu7 zxjh(POZ_U()?x`(0&TB{k#?-J3|AV7Y;Ug8HN%8i2x*(QS(&Y=6*~APS#&(yr{o^; z14Jgb@jBp(a_Pb^i($Vh5D9)pn=ZSN&u6#`j*K=$|3VSYm2|WgrTWMi^8#(NY^K%8 zR93O6n&4+>%fckBb_xyz4odNuDRg_hH4`e8zD-E5WU`o;;q?2!09=;M(GLe!*D+LR zaF-54e~-8NYI8;n$v$#n^TL9oi9?(h@fkl{*$Qh7>8QE=b@%74EN$Z`GroB$3qzjO znjB$D$_ea=8Ujf(j-YB~qoa}x+jZZ6d$JJonM-~5H(hk@eX(ttV0IAY+%sY#A~=iW zy|ayikP><9vR^Apj|e?pb?;0A?T_Bypq|Ng;*y|tXyQbSeVfU7@9H9=a4#IF_gl&C z&7{K%P@=rIxVSXdlF%z|WRS+yKcilf(5wnoTBEdnUa5{IB|hNl4JJ#e_w&i{4vs^yS;Ooa6u^5({+3C3jS5o2#Qps46+&FA{=M+KtQ2R@ z7!=e-*A(zqrs0X!TaGY+b;ZT2madZtN%W&BQx7u|EzIatjU6*+W9o%h%lvkqk1e0M%9@6am77I;(bJCZxAAUZKyf8` zHhf~L;IA&4lOb?hBhznh!(6BL`)Ul*ukD*jkK+NuZ*25zJ{ouPr=#jGZ-1Mip;o@P!nrb9E;(^oy8hJ)xVMZ=a z&AiCUYRzV>N_aW~tzCuZ4et{gr05~|1p3Rditlf#x-fbO2d9)tGG6xnxkW3J`5=^K zQ{uw!hfM)?0Y@BftN0;q_j`qEPHDyZS|DQB7==Lu&=~G6WoB---GSJK5|93&$|v&8 z%3F8NIIi%QghSQPD8c5nU=;Vkphiwp>!tEfCR%9%XAobZgF7Mfy&VL(&Xr%EH$h+E zvKEo{PI`O<E;QH3j!4I$(0i)>~=^ zSl8_9mWdR{xmlj@L9u<_%ZcP+Yd?a3ZL=q2EQl0R7RtgFfKWaO%SJPBtUF7`ODjq@ zU&*GVw!2m-Cf3!oil|Rd&sa$#V{c<WWuDFG7|2Yx zr}iD_HwLG9Za!R4#u|Iot~OeHDlK8ir8r^%E0~TdAz6>~X(EIMmbkYJLn?{Fe8Ay- zZj&jfLUfR^+#`e}>+^P~yj+cO-##LoG0IWmJ-}#YRm#=UtA%*!>uvLt*&z~I;j?2b zdO(2yMW&|)xhldTpBEtWU1u9x0h>V`tNL%NXb>)XI=nquI+6=E;n0p~7ccO!#nucXoB<5!>7+u5CFMImH{a&%Y#gS0k1R zv`7Fah$hTW;g50 zuv~4QUwOIp1gULa}GNx2YG>R4Kn#%RFa&%%?Ad zsUhHe>*(@W;6O?Aho9L$FeVkA1y6iIf0Kdd{~a|@UA071KqhV_7L7@*iQ=&_5)}~ItXiJMh**6 z@480tcoKojjKBVT_w()6Y%o8J<2BduGI5ppWR!h!#qQ)dzjzbx#*O`&}fi7)~9zjlMSdVQ3>k^obMOBdg!Bqyfh>Y{)R}W@#&rmvGtAp zf^A5*y^S8`b-YiussL{vmC1laTJWE{+hX_nNHJJ1f?YjL0|NDwEe`Z&WjH!z`>^d! z@xBV{ENo4i)A+s=agl*{ot``^$cK19td1~Fjqmjz$ZinB0}N5Jx_Nb_fbvb3^Lg@T zC7;Vy!n=JZU<-VCCO{zsme|WR!G{n+7bYGl{>8y?7Y=Ofi0L^TkCEz>@EJM<|0c~899#lqRm0$lsPOf zV1z?m_hR4}!Y{zzJN<2)nnZlBPEwSCHYOp9?!o(ZAhBaQJIYv0r$v+;hF#ABmRv+> zuj`>h;=g@q*1j2%K`2PVdR$7~`+X=l#$4En&Q`@J<=MJ2kpM}#G;)YxBZ~t|UY*P8G#}xLLlU}gAhml6&T6Pf~maWtM!11kF zV6#J-Q)T7vXudR6`8wiiB_7mc#Cn}VDbZcJJRs%csC(7o+wiubu_EmHq!`;M(H{{o zu)c}mcAA|b&e2_ycbDvzAsbeynn04WeJp1~{-Pms1bp`)`wiCTEa!bW0#HJpHs(gg zM;*-gJMT;B&S~}U-0S|xd2UKcRTIO2{Br+9dR@-jt;WR@^VD}V^n0)Gv`+H$eqy@( z)U#~aUw-nx&xTDa3emAp;GbVol1BNGz%>&V#rO}-xoI$YTkX*`^>d>7mo5dX;1bC5or(ahZym4<_O2bbm;u9*$OBI4ZLDl1VqNYo=|8hp=F^+jZq_^UAHmard(uGBvkNhbNua zcH3`9-GVp~nQqwTzSqd{`kMUq;i(XPF#LM|KA+x(FNZ`?oZad4d~5#RKJ7aramScu z(C>I#Kwe|Ag~C z>KYT|I+)~)>hsP^`@z!jq>py=^62X0@rf<$QmTQET4c}NR9cmFvTlFhpS{(lt{hKo z>NVB-^mNl2Xop`t6Z3r~#Kzj7eto{(j-V6oXVkKCxnI4_gQG*Q`{acCZT);(aYqsb zGKpTi$;HBe6S*rSP#Q^)L#cx*nH>-K2}weYC^ms#3Y)zBRov46{}7}@+xZxiyako1 zFAf@O_UC!9g8)X8fUJNb3(VtT9`}2*c-Nbnij;-nluj`n-p!v2fpyqsq;m$WNh&Q5{F z{-jDlo;cQN9qt!Wr0?m$lPpL#mN~+JQ87$Qzm0PfF=$|%P5D)7ptn=i%cG<4!75sL z$RAkq>i|;4P8d}IKe4v=PHC>fVoI=FPgz=|n6>(Jj^hT$=IAf|_+CL8frVv=LZ_3IhKUk(q)PL;NlvRYxI}T^UJw=*5^Hyg94LjW+osjF zp;;BZFGW@HFB~x7Q%-H<#ERG4hL)l^;+3Ky*{ldEZ?%LuJ|QuN5R#YU6-sE18`FyK ztv}ljhrE8ex?L`Brnc6{b38E;IsI_mWbNzz<4_o5rn9QF(H;8FMGSGf0qs^ouvm}~ zFY`Qftb4~h7mP1&KeUvN!P>B)ml=ueakAJ9s08zyLuHTN!zRY#8JU%Dqh2{;kig6* z((GogwXrCWr((g%IS~B=!s0wjTMmbsf0fI2s3)^d`C%pQnNj9BBph6F%*90{FVsR( z8qQ@#G$#U@2~fvwGQSzZ)PL6knArC|y1fZMsk7}Ur_SYb3P@znK56}R%P4I`D=zwr z9B1F^)R7@k95a8vrmTP^aCFPi{`5M+S>I^yl`lL@f%u*GI$;ZQjM_VfFBkovwOwx# zNiPrzFy?bckTXvR-LlTp#AH=WDjpluPN;=Njw?i{haYyc@1aD+e_ovwQ$t1k(NEV@zdr?dSxK3QxT)qmXbg2MCv9gUGz0IKbzD115MfbP${Xrj zXfjZei_|)GN*ZeCzPMYb_iy(}v@GAO{42C-cy|tg_flePv9qCqo*jQg^Y$wvG)!UDQ*h{4J)StLQ$Q zZ_=A;1czF3fCal$2~r9=?o*?g94`Ss-e+b95~sO!`s`S`;(u1RY_K%^x)W3<5P#z3 z`rBCue|H-TbKlz%ByOvs;13?q56lQPG~58&HfQ&7B^J534?jVtkN2&?q{guRaWQ1E6eAoMB~4}HiwOJJpQyxFXjWY2$VjxbaEY`uET!R+ zQEJ69$aS@Cx4?Ly!?IY*an{L}x1kN=$3;Y|CT};8R)CrPB&8|}ath_dg#^=GC!q`H zhOnPGwn?(;zQ6+$*illdrH_S8FRO`#Mu>NlQlTct_Sm>faU1i75+jL$Oy8Ci^2 z;VHybUW7k)f|Z+#12-9m82g*42`k_pj@5y9za)enuQcj`x^w?(<2per%oUdK zzvz1B@VuApeK=~8#`ZQE%Y+qN6qwr#tyjlR#`=X~Ga**!gfK3A^Htj{dW ztb5&SrhcvDfm$&2TZ`SOu+m$E)n%fR0dwLs5^5XIEPfCO6I$)hPPeDmKAy-=lC+`| z5%=qOl20yL%O8YgskJXoOj#1pr_MTc5XSnGcU<8IT9n!1d)3f7t)d&3Tag^Wi)REX zebwe2Hbfhy^RLO|m7>9q#nHh=DUT2r4v%SfCZwopHsSnRagaF&-!C@+#rArdPz+{3 zDNoNESx?@DtR5d_^@U`$A-AbDg7u-TWb;JF4V5KSqZ?@;_kNu6vM4AG>b$XIb(^%_ z#7Mv@M3#-Ybb_{GOC?O77?e0F?=9$1JLxfMvz^qpKz6OZ4#6~nLdrI>DeCVNQL?_hQII^j zK{XJY@p-X`oI>P;2f1nd{>le7n+qt*O@%I}wDp*y4f%#iKdW{WK%=w7#lK4Ps1W1U zbq=A2ixwDTSsNPaPgj`WU3RQ2A~)kzeH8fy9ah!E-ZE+`gjjLQwigBbaB{y z&`q6N!n>?B?RydrrkN*wOm1Om`3e!#i_Lp=fMl(&s=5TqCyhbyS0wy@gj6*YfA7}^ z896yRE5QXm>X=fCFvg@?+ z0oSNekZtUxiae7U&{6XXjxul2V7n2hls@rhn+JKz5*bl7_&Ke?T0x4v302P8(OWEX z96%Nwrl6ya9V8eJhK|fj(lS{x1|B1heImMx0 z(|;c=C@y|{e1wOGhY0f1${n7f*N5!1pZ#7re#RV}sjDbJl`w1`oGPPsiA$w@j(6tx z+%CW-X7)5w30E&CFJPQmcRD8ET3w+8}x0?7kE1N9IKGbvQ1s?p+@UYacw%hT*+R9$hDvo{QqW5|L2z?f|(5d zyz`2mAl-LV5RD9x!Wi7ECVcIz&eR13;ilEElR_W^plk>g%jXWt8 z0mC;TX-(YvserU#K&1*`j{+>G3U~vY*y2f(sDq~C8><_D+`lLaz6P2Jb7`CofgeL(xh!@uW!gke={W82z`T# z8Q^<*-bn0sNI|^R6z~j&@^ielPn+cBQ%czaN_as9+3u;gx4v2M5aBM?IW=Pkn6YhK zZTCSKmXRvo6zyg5S^1c>zcW^Y!H@M%S1hLQ>8raRR(Ag)ZJwye{%pOU?Dd$9{hf_q`f&)lZ?>C>|lXix&trTLKqCoxB6Q!J$vvCaTP=O1|t%-AYPbq4pAYuX|Y%A6Mig*FQ&GcXq)FT`vdMW9*X?nfhMVCMrqhdbDkPko4Kr`&tsm0JGIfBF-^E-gd+^n0D(1+{*Gh6U*Hd~zJRL%Fdm_%sQ~b}=TQ2mvG9Vf+_a=5L+u^N@vM@PGpXLuSmQe-<3rM5W{3)4S0vKJ+>~9F#tUz)cydg2?#H9J^O-M_n8hP z*!=47eC?&B?(4gzhHh4B&|ZXuEq$fM(DdI2_Wz6mW3t^xWaMoOckpn3`D1tOUlg;P z3Ei!iWM8dC+6x7B6LqD0G?8#UAzYpSrX6UD3k5=EfIa``ApiGc5dv5%t+ZAKOX&t2 ztS&SNd=TM;?qfie)$-f@&%0aSk9%#Avdj~r`~n|KZQxqTtu2p2Pa-3{e}TpSH_E?O z95B8=grA?LC214jmU1+>;CRjZ16!}7Q(f-nib|uMg_5+85?H~?4Ru|j0|_YXv>BCJ zFp(V)kp@XKE$yiw4b83&ov|VMJ^t6!MSqI$9hBcZ1NdL@M{Nj9e0=B$?d%+jfgro` zba|vcEp2rOZt-HP?DFSX=YmV=76MlYac_e^v~t55_E+|-g=W$Mhr7wbPXFfgs_*?t zWq{HP7gm5zq|%r7u|l-F|FE0CSE(8ajN&eUVl+Cqk;5PKFEL_3fbT*H5hZBm&^kpV zVNJ#x)T*he4XxW8TJJT6(7WzIbZzOD>&skjFJ?X_H@Py=SM$9;Ay&d#+^k0bvbX!? zFB5;ncbuZm<2tx5UM*b@H-KN2D|U^wy##6R|2N|H|Ma6)2G-iz`s2qBaR+DOjwLGC zYG}aP7J!+hHLB65l(CeOQ2Rkk4EnsvU9J}-Oay1GYAa=`Dn`ou+U_;`PEFF=(9@U~ zImFl6>HU~LRaOXg$Px{!-8pZgjSEjxNkL03cq%`gJ4xgIqSw+D@)-n3O)V8^ z8@TG9I@NuO?vG#Fy$1@jpZ z%I}T@1oD>*{t)3nv|qO5wKbI#auE!}VCM|I(QTcYv`&X6>C8_;+vG z??b8~f>3A=`WHVI?c9#qw+_7p>?lx)l1&4Gk8E_;>n(mK*A|+Rk&(Qp5EUYUeq;_L z*zZFCf&8rvPm=99=Z@_q{LMvfK{%tId@;Wo|2PL z2mzCrh1+< CFnEdok5!&#R;5JQ+sL*EHSoTX5xSF0N;xr6bG1`KeHtvVPiLSunh zq%A5?ZkgQf6)&!;AvEJFVI$M(*n*F$6@wwxEk9u{1OI8w2+)9SZ0_@6QL)^yL|b6; zz0oI|hSqifS*ris;N!=8uutaEmfKn1ANl-zT+If6DLf@b^8dE=bTMsu?1O@BQ9TmVd@XQ{^<}npIUc!sBYrEcy}bz674C@HQUrN!Rge>)Ly_ z7xxA#mxvhu3(e6pk-q1E5=#s`LO(5{FJSN{&WlnO!AR|%3Kh^)sKE2}>({^~CuiqR zvd|7K+0(9b+2E;%^IY>X~td+}$ce=J+mB3yw2e%qf@;42t(AbkJw3 zTQa)X(rxQz@OT$f2>Ds!=d$LsC+h}VoG39w!$OlSW-S<>Q-Wi!LN6E|$=NbD^ktz0 z`MoM6O1iJhgtEdwY-+;DA9g(2E)}b8`|7EIxC=G#!2H2SIYsp+)hmPY`Zz8-FA+`!F1`qI1xEtAuTdfz+y9 zRe;rro1hcOf=;JX>Rr(xv%T9@aO^@ucbdVNl)^a7or|#fTYzsg2V$&#Z*@k z?Gs_{ebWsXfcw)Yms5gdeGZ3>Kj*qCck**qDlk@-{4Ca!5eUw|vsV8pdWrCpvXhAWvv4gaJeB`V#zQM|3Os2JmoEG!6$+ z-Dwu#6kBjn%+W6PL>xE=8_`mb?=^&B@yGqVw6_r8(#c-RfTK2a>n#suMe?0Ps$n=# znNM>e^r{!^z8gH@yL2B8#mySMYf4Ej4`HzPPimj>pII=_-(Snh>p+b`-v?Rkqx|aH zB&SyoA=8BQb;#^)Q;?g^eA~k_1fDjb@V0v7T}~i^S4!p8y5C>HSNaQHyR=OfdjA*{ z^sJG#sRrr?3=T^yf7HbW;$4={^jH0;Ao@<^NbUg?&3>qjO-vl$D8`m>fS{xgt=h~n zpiXoaWqtpKHO$$`Um`y&XtFwPqM6V~>SP^>T9V^PxJrXk#30JB!0UoRJg0Maa$Vd3 z>H1A=f)~&LkZgwTZ8fdk;h$W(t!x)!gC9?BY^!uSZAB0wWP6v1XV(EBsUSX|ZbaoO4oFEHCI@*k^q)EYJooKS8INB~Xo7NRezUI>nu zT0-~!g1h$wS{HA)WAVrogcL&ZB~dO6CfM2}M`rsZhf`Ex^^{;>WplwRyEOiv?=W8P zb>5X>061q2rDlJwxP!`;XC*0-CG?5qF-Q$;kj5V(w%ah|lasb>JMXC5+bNQr{T#Ax z3HjLrdvrHqWh78Z)(g)uWeS#3oKjLetze`4W&#n~MeqWmH@Jg! z^Ty*Yw|pr(gk!VV;Iwrg{)n+FM-8XhuZPN~`tS{K<2DsH_hrvy!BdrxO!xBcoRt7Y zH(|Ayr~XTLf%H0*`0u)M2FHP_9A5s)1P0plIPN-QrDPB4xXc8vofk*w_t2mm z8W1~T$i1;}cbcEP%f5Ez)eAB-srj{&r^Mo)5lt)9s%F#8?+D(m3`(Jkn}BFR{JpLH zBS!cK2LK5f@!gokvnomIZIYg^hE+UDBPCO>+ z^cy3Uh#q6qhaAW*nD@X8l&lX7ZE$vWCPfp!J~6yEpF#^09bDh)GO-;!wEH}6R`i0a ze-V>I)J_|Wk~a+l-ZoyTsoo((mcAA48tBoWMnheIIS=v=q5l5S?M)6yxay~adU|^J zNktU?0&i2GMhLnFO@Eywwo3nXToQtUc2HJwoqXTlfF|_6>h&rYaH~)x+-mS%YmrKp zIsCfukl5gFpC)Ytv;MB28-PtyqCLA{7Q{&rW_aWZP01lwI2egrtY; z5XNX!&E+5m;g2J>67yfV3yw3`z?cZpKWJ9K^zGC{PeDiP%?F87j8Bc7U!2E>43LU^ zqAElRU0hI>NtJ#V>QgDR)|V<#rv*_`$+j6}GM4}VH(J{Mcz*~DKp>}neqy#M{jB&; zXCDtA_UN=!r8Y6_1s5kB_+!(&SMjen(^R$ zoER@J%-0^-5bv7VjR6aPwEm=QAj3}Y-MR89?wKbeW8~RMcF`%^E^gVO%~>Lh1_pL+ zBGho`+gtFUpw^+H@U$+)oROxJ;Hr!lYmrJ1A?HF|+)0*L5W{DG|3M=#f+15zK|26^ z_g_|!l*;crR)fFU9;X{4Yp~gqTLZQlPzf8PZ&LoVdYwfrz4J}CK#?%IPd~yHsw^EG z+(Y@8HQAo zR1<*#4hDa3EJ>^=#h%h#SnqF4(OLCxe_<7_L_b&gJ)N%HwVKu{l#ODvX3ZI+o3g9i zUNniE8;32}S~j@xMul3r1+afaNXPogA=HnVr_!sTGsc)2je>x%&h@V@L%QU~%7cRa zF>@_P00uxrkxOEja%O$7^|58zwnjcLb%Y&+y;c4&kgcrp6v2HQ;SM1hPTnWq3@W+X zE-d-FKhK`#%fqH?&L2m>JT{~6g|k{!P*3dyvov6cacbH|Xnd26-%S&>_AzM{M?k8P zhS{*_;J8r3tCAG|^1q<51}VRqwCZ-TCKa=NbBqJVhzD(Uz91snyBZ>rkZyXjG*Qo- z{UdbgjkkVg-jjO|N^r5Nk_ZcVp4Kp;mEp{2R6csQI^IhTHb%>5LmX@8{`5*aL26Wu2EI zZHUg7Koj%jmjpYIo)TE)LN-}->xZDU(KRt2qS-}9VTWwPL5AtDzhuGMJlh-{oVjTH zyfnQ1RL%z}q}{Bqsbro;Q7oTs^OJ4oKOkgZ95EjeLm_KI-85AVhoyPT)+X89e_sv* zu#A7mR^17?zi0-1eVkv_<>H(GTeqEf@jQ9!%+hj1D`K(&31hL@uygIEv1+?uTe7jK z#>HcOB^`b3wthFbT6&zisM$2LP^d&71%uI7j}Z(Jc|iKCm_hI+A&03s)}o}pR1=%! zzy9qT_u?1i(J(9@?}}uF@)5(t*^R_nuLgt_P6MZ`4nvMj>%t_gkJ?2DxCh|h1pHfC zH7MYK5hMLDaT<(bfq!R1T$%&zmY~kZ8JQhk??GB*6CrT!ag}^WuF*JaA+=Y+hdA| zMqA*C{Ky626iNI!+lm2#@fqxQzX1XiJI;}t;T5p!=Z=Nlc`_z3=SX57+>Dddaup5U zZ?SVq8t&Il8_d$x{smw+z%vvD%EFW8GpyY|_G4!YSBaC!>zTo5hpm87{V^E;@*hC@ zYl0oJ7c?4X0iQ_RNP(`w(xi}ff*sf;pXZD3i4Xeq(YdDQXQy7kLdlDr?|OuPSQH@t z1`(iSv}E+MX_tc7p%W2n`tLtlHLUURF&Q{OSj8Pi9cD#Jq}{#!)BYb0eY#TRA9VXs zcs;RzMH^k+l)Xd2Y!L&3u3$ew zH^PdhOVW|4iSZ!{g@{$k;N7y_S_5=zY%H(#cU@3z4S-BMr}NmR%oZ);VIQB)8`H^N zJUBf8<=KoIy%b18E!z5Y=r)G4bpeD&_nq+!!tmGE7l{pjnN;>@p*AfHS$Z>5?dZ!$ zrPcv+VDoURgErq;r}ik``sc#$o_(4 zJrgE{6tLAf($n#`+D(gX%#0MPmW~G@JcG((b)ps=t-&UYGRTlMGOw#~KtWHfa3}g4 z4D~Yp;KbEQpXnE)k}Pq3pywRNPAx25SX2(tzOrs?X<=KYE|0DhEm-6yG}YG&FLeKR zZTfHY5td}8Jjyfu3WB-rd#n$UJUHXPXQ`_Cc+j>V8A0@)rX3LKktKF%=v_Sq-@K!Qy zA;*mSe7)G~0dZG{@|~t2TF0&Hrbk&rfDXBi#h*W`5>KI-L4p4{)0G|qHelRwgUJcs{8-0CnN;| z2>}on=G-?pruclOR<5pN=Xqtyd&3!OrS66rnsI^806(<pk5)>EE;xXCj z-RR6*NudP@Q~y_&`1i*D^cheof#>dEgUufDpW`Xgjr93Q6_eBOvD0TZz#Z|nv!-CG z75)pYwgI5kdZ}!Vl?lpvvRA;*nyF%Ei&xSnFPUvwDMCa~%gGi`MC-m%pRER^e|6F| zl2~+h#l;ViYreFfM}<6Pq@*R+I4FqH8g^OApwg~2`)(~VFa2!E$4^f+Q{C05b3r}J zbxY6oIS3r@J{zD<6|RNK6h!}B-f~wEilss9s%S>M%XR){vuKo#*yrP^pt~SQK(#^b z>yoJc#zX7P`jrtdi@#TQrD<&4G&fzURXcbFmGtdsnr)QQ!i2bCzA+dtdq-+=w7UYfB0gnfKbF z3=a<;tfqx${8c}SIElw?F++!@pL?+Jv!cq|9+^b0FM^OFm3&_v)-pX8728{oo>}ZX z_GihDg6@@+TrW54sLXz^zjXHr`SlE76*AuJh@~b50!_~+*oQ^At!sQ;3X(53ddG?w z(@($G;|^3NQ9`#`lgQ%nSlMQyF6AF@8&j&`5{ z5#yVySM$N_*R^;3TDP!ikl#)-{;x7X)*mn9Jsk|?dN@PHm(m+UpC|`sxX$A%V}ViR zd(w(&%hy^{!MJ-g& zT3@{y6sk=wD`WsRCI=$=?`EtvwA!ex0=?XzP#N0Tn!qJ+hdF1u<*!AalR zjIL|idAzTNILD^A7>>dEZo_H{Npu7DL{I%$%6gUNce-gNkhHX4(1=~Zu{#;|4#t{y zGni3&^zrR0&CW!H7+T2VP3z`#@KmY}3CHf3=2!3@MU?EWHltVpVmNVUI{i?ymkc7wF|u3Z-G{Z>Z|gu4?)X zy!z{hP(?qynR_9Xi*>kl66|8Y7 z+HT-wcj<<<@ykZ?)1eGZ;^Pmn!h8j%B|ZP9S?z2)cldT@z${(dw_mR|2wsWP1t4U? zKSaISW#MF+C|m!^t4Bi>r)70}?~u}Nb{dM2iyMo!ou^~9V(ms`oi>!&+`&^Q`80bMNYt$gcDo5 z`jPC1LguVRp|iwus(^{sKe!>pD^t-jQ3Z7i6^HbEC% zRmJ#1RjJf#AX(GubF3c>mHZ|2P)!0c%_Ktm8IHM~*k=4;@;%5D?j;ZqP<=bK&6kQ> zu!R^?lDm2m5ntxe=v2R1#*d)8gjR!Vc1*S8B+!Exm?Gpze=jDthvDt@rxK43v6kRg z37`t~atV%c-0+!9DA^L|?zugn_z~uN%-^Tlo4hN%n{I5tYHPJgjIzsNQJK}p!`6a8 z+q}EV$+AxoDx?ll;H@8vC6fK4z(cDRG+;urKPkut}7X+eGN#(TM7NnC^J+bC9&THMrWCn){ z^miXv8uv$Nx{d-lgUPqlV}?#Lp|UL>Zl*-Q1M?d=502v1kOhPi4tZbjCT; zS@jF_JX6UgC9??@75VQLdtf4aUJA9eQr5b5VFeLBTKVz>$|>UC<*o&V4ut8riISk?p`(pS z{vkXF>%Ehv)Ig^?F(h82HOTA)ON{&8=a@T02~e~hy-fZ!1+%L3EXLAlE_b*N#KrDN zGYHBSXyiSC-QzcGkzL49TX`ykg}j=f=WK6UW`*G;*Uyq9>!2-fFPf~%A$$p<;`aX9 zf;TBfhE=zGo&i`c#EszNA|BdLX>{itd^57Sn&+{;Wg9h}9@vLU8>n^8Rqjw-_spZdf->vEe8`Z^`D zW79UKxC7P^M4mr!bg;ds382XIZB2b=)#AJTI5E)H!iK~yAX07uF}u6Xt^7q#*H8*JilVnEg2arS$W@|*l|@+5)Rqe8EF>z z+4Pm53*7qL?e&!pFE3@)2nBCEp)F>@EVqd6;|8BLPk8i!?tL;A%Wx^U%k2``r0)d1 z#m7{g7c|Aiw!0*0AsOEDpIQbcxFSGke)ynX^18Q^lh$eWaChk0B7>g)Qf?d$t$g5V zq0FA8QAbU{u(~qMh0vm@cZ!ANm8(Yk=PG?xjn^HwAw-Mn}qAT!jpA&G3D6 zaD>npI1ViuvxT@x#mRlioqGSOQxwGNFR`Eg)oIm(wAID+rs}>mP%KsW~mDYTHR@NE}ORrlaN$wbDfen(M1He-PJM z34vfX(7hk$I^a^Xug%#VuOEQ9fFS&`-?9?OzteD)$4~fZ+U2gzf)T}9sE|0TW>+c*;`yjLdK{!m~-b0x@f`| z(#mddtU84sXt+He1fXQ2{O$`V$LbSCdEn{jEQjizFQ!X*greHtJNLMauWCC84a6(! z&EK))%Ok6jZlqA9>0_rwn&!t1LBQQ4%wXPN)Tey-;Y9gr-ez+7e z2S>Bq10GRH7uTyV_Ah2f@W^wVedO_Zva`W*F#7%lgf}b#ZA;=g_wL)(1qUzyhgNWpe9t$!%nGi7`+Re40kP?Oh(I5 z|I!1uMJ5BrAxyx8Dld*noAq~Ut+h03m)b5p5%w4~MLafC#wdpo7QYK~zc>Z0_EvGa>8v?A{TZju}$7md_tAVSDu@3WIpfH z4tj~zRgdTS@0n!sBq@b~{TTtB3iMK#*Wr-jq7wZy-|p7k-ccVvGS?L6DJ3gZ3PrR< z9d2spwmv@KSFR})P%0TuCsTDd^}pR0WIwH5s+t@ms4BD7XQ~&%A^cts(>q1tXDVoa z30GJ}#qjj+Wz;B+yo2{)X12Zxri7UewaSQH%?pm^nWy(QT^|+?aq^@|&}PaDtva<7>G~ zgmlS6KZ`I6QsBw30)2#R1Ba~4tDHAUPA3f`Q{d9s-7K*@uZ&Z*S+1x%2I| zCOAA`PheWQGTP)-Dnh>2i^U#xCQ|DMLyM7{4yVFi`Z23K8dgm$AMx@~q5X6?S*w7i z`?wbIIFhO#s^%?Q&to!PN{O=SyP!m)#TTd!+W8dM#xD00v^}AbwdejGBo;|G*oypo zG#FLob9dgzd%@@WQ%Sgs5*i&Tfw2dP0!Uenug0mq+%U3JjjWC1Yi1}O0b_U&r?*rdXLz~R(vnz6M$ldf6R zU9ZUIyPLnR`bq>cc9kUPDr@Mc)~#q`h?S_kaFf*=TNwcz17c^s)wgaQ8xMUuM48u7 z)vKowoOuAWY2qr)#U152U&Prd6T={2C+w!cxJTE_ah5&fGA%fvm>?+a<(zHSy*p#h z0cB9?jjvOf|Mi!>>_9-M@MQ2yrQ;Chz(*;~x*0FHux)GOPFQPoP{kzt_)9!qd0&fQ}! zWD}{w7kgN0Ob+g&g;I8SRcI&5g3O$zz*iP|cMgZ$7RH&nK(!@qENTjf>&&K-vjaw?pMP%=oNG{MK}I&e#ymw?^EPm6 zPDTR_kb`;cWp*pm<;L01ftWjJ{TDYL;9y{%w`)-rdJ`B9s{=`o#h1W$jyvrt-=X2H zeT($CsOMZyo%WpQ;dW=E%03Fu!@>75M8BYqO%HXoEilh4Z7lAke0xqx1C{ZxC}=l6_JLK64Z0RD4sZp2{aUw|>Lm~r5ueqM!^MkT?jK-& zAe?3khmwSZ<(W5tfj?B<U55r*32-okJ--0kG6=4$$NHfJx_wx~M<+?ik(758HNp#XWj9pNYu!vU z6pqX~mQ09?y8=1%J_@_h7ZqHf7z{Z6|cYJ|JsFJGkk?VEHZb1e$VAaI}-B{D+VnZ zpf-@0R!x;(TZXj{w^KKbxP?C1zsuNna!DRY-CRrFiiWJ;!F+kzJ=i}-Eu(ZzJs!Eax%v3`2zmRiVjrtWxC8Bu z^UaQHJU+rq^n!OSx#qS%7{?3xWOR`!Q@bP1>vG_ASRd;)CN%1N=3EQ&0ND03sTbRC?MM*#f`{=PDB-RIRVC zH`!ja#^&NuugAWgzZ5UE1)(f{FOw|SgU!y)vgayLF^uonVZeYd>M_qj-Y`jHnS6WL ztNo$PVr+cSLW$&I-&)a8R@jnjdwk$6Kb{=6yGjsO_i#xwHZ57CL7RoG!1rjUGISh& zPUzTEIo5!v@bO-UwbtN&`1PHSZKj{D+DZo`w2hV37MC^HxXi(tARhI(VZk`iAyv8i z7Ea5%F82$g;_+b#n%eK|!)|R7f<(y!2X`}I(fk>kraBGk~0sI4SEA(#_c_0ve00Db{Lk}0H<~vd23@aV&2;YNQL#I}9 zgcr(TR(!Kn&i~y7u=ubOI*fo2TM1jW&=Nv!Y)xICFCLY#Zw=v!5(3+G4xi3On3waV z)5Ejd)3bXk&?G$H@8ox!3g|BpmN7t@%d7M1+sjMDX?ylj>-IYC+1k>u*Gx(%2#JZc zvG26zR-jwa@qN-5NH1%MV1$?on5RTMgr`NZOv$#F6~pIR!k&d2QBq&?YGp&N>~wL+ z>HnKR__z>JU3t1eU|1^KgQD%!D!OU?wphDumfPAH~ZbKKVq)=m}YEj<6W1uEE-Qa)g=HSeTek2jM zZ-I!1XQiy6qSE8&^DDlw#aL1`Gk3Ni-7a}uCKL5U%xGs-XMpKNSZ-HEN zWL$FRliT%;1%#*dcN?h*YP#97=M+~Li-H>K7_wQgyZ!=wZNMdcx)fjO0jW(*^R}J7 zR*Q5-!DRV{6sMdv+xH>%fq|EsuyNA(My>YQ!mvKN-@WB8W%+-R^rI%~h#y3hvok=R z&36?GKpbdLxV?goJ|EXa+HCuBIScROd9ab))tXTEpu1FfT9~`2I&U+D z9yWK+0VSlX))f(UA~_2t@Z`|D-mjvuF zI%hVQGE`gm!@*#GT35g}Cjz;haJ>hO&ZxtEcXx?~pP6m1C-}7%;~5Hzw;hv#2MPpf z#j=m3g+7=xaee^<%d^N#M}5SF$L5&DcAuN9<*TQ|SFZ?^5n~V|fBk|w+hrVfR^zV& zW)xxeIc00x%gBqV!~`Ip|AzqslW(82yi4uc9uJvyO~J+IJlsTT86L z>U}>Ll0QVhzwD?&2x!7o@oS|ZD9`xZ-p@XjjC`6hMMcAkb3|M=B5iE()-u2|05Gmg zMwBfnON=~D#kL-XE6xqfFb#U#ximuAs%iNX73)Is=vc4PXbYsp`4 zikDGsMwli4!>m{#fWxp}f3lg1qT`yQMqhlD>99XxP1SfFC>~4F4CaQ^hmO3i4Wph@ z#K`XDQG3H>@*;KT)@ke~DHNoYOBx$E`@v5WKLQ5k9xdGp(GW!RKUQ}c?CHDUc4{hX z3hD{X*C-Wp?UcgL3PkZ;vyL)6hC>g$uZ%O(dq!HYW(F#cc5@5&k108yznYh>axyR) zXc$!_7oitWWBdwSIv3cg18vg5KkGM}F9aq2p0ZTRY4^sThJg?0pRX9}z5vps=-2{k-SHpuj6zM+0PL2$IzX>)nh`62~XEMxjo z`8f63rd*DRFP2L|LA8UPsH}Eq?SejtSdz#nifxUFi+fXc|I8wvR%^(9abIgJ8tQX` z9*qBIC2#i-^SiZH`8b?*SLP{~7VgZi%*jLv!CxxJRd#ZjlFK_yUXd~`ifR;9-}b&{ z-@&UBk@h`mzGE3T$zUHfoEX)>c)z`B%EWhUobv>gY(ht`op;j{239PU)p{7}!~c(2 z>qw@(_h4SC(Q*(_(dE4B<5=`7BYDU>o$5J3LuVIv;%(nwOzKw*R25O!vUPCUJoI7u zL?i=K=(ZgsW)JA)uxO|+#Oc=AU2g`%()B_A94mmps&gzslT}q!3*j5J%yC_l7w|Y; z_U)$1vTfY9U{NIdj12P3yFtMIhk4v11Bj5~pLM0v>9X5rcXK(d;`n?yLxYP93fVlg zMqgyWVgIaEz(of{2u{}1HlvZ`sJI+CPVNj*c9UeKD_v8OWa5PDpiQiLK?#LOJLvy) zj0gIcFxa95KUcfWsxk+|T+b+V6gxeAIpYf^!g;Xig+}94p8kj9_;m*%0%UHVYx23u z)Wj~rK=~tI4+wCN-32nig%6_qZ!NZfzW~AxFhFY;Xn+^n*1y!|{0|wMA;7+YDqaZa zTOm@(gsB1UL>Sd)0_f$je7!;ofjsr2H{J^|h!~O`f(_O~m+xqElt1U<{|5h$1;6S> z2mvhB_m^SfhOC{_8u+!D5|h#OS)H56vClb`hb9c~W@pfM{Z@9X8&z)%Rf=QK%BdRtt8E+Tn~sm} zNIVocm?|k#sQD{i-r+p~lE6!%eiS7CAq)`MzkY1X9{`evPL+=f%-q5~hr?=T5m0coJy)D@(tJNlFIjOzt99KoUzMbA5!r-7kK0}+! zO`_R1WLm!IXunEpVaf23O|8x)?7MUt z^dOu+dd(xEKOk?Hm1%#S!-t^qE0TGh>9Sd_2M_})4<&|%x53hk(Y{!Ci=01r<+5vM z=hNS%f&|h^*JqbsB83WDMNODMTX_&5o7E4G>24<%Gr$#m=uCYJAEnv(3C*$fBnx7Vd)_ zuyATR`5En6W8K~OZv)xC?zg;5pBKL#U;sJ+3Az4ap$59AGG-7A$5@S@+UG7nY~~}D z+(^;g#OU^M%XU3x?JYJhC11YGvbC^q`kd2V*ntXxeo3&s3pK^#y3Zp2G8PzAQc(EZ za?UM?Lvbkg=Jlg}6Y73uXg^r$)U87J|(0vGGg3ce!?488Nv z4t%RWy3SQ9V#wYqe=getpRd7H!>?v+@E>)x3yMX^{&T2{_XHC0EuP6~eT3_*WBcMm zA;$uEJh2_HTd^aL(uRERxv_S|tX#wDnEP;q>bWycU>e6Og2x7VlxI9YOD+Lg_qVUpuF!k!Ssr zVFE!~k%0&R_=EW`*V-@Jty_}+A6@4hUD?y@{YfU6*tTuknb@|?iEV3Q+qP}n&cwF) zp5Mj0?sM-v|DLt>-e>Qw>aMP?&$qIGUG8m42LDG~Q?Nomx5{P(O9O;PUeCB1$?T;g zukqGsiL;jr45!dm6lbra)qNu99cAhr;!u;KkEljXATQ$z3>_sZF5O4f6uUl`3M2IK zNsFShU7Za3>bsKRo7EB)g7*i%JJ>D!^IGWV9)AAi%l4mhDpYLt9&o?`g8N@BHk-gN zhbq7ssV$E%TE7qbF)m9=BTdCHzhWOs_?Mdk|N4JKL-E)!A`CJyQnHh3Xqnhy8rkXP zhv)v)XU2n9u2+&ixd3H|aIc7DS3>efXo; zOR5^u+1%W&yECnqRPs&aMWmNkLK1!QZXUU+;A~E8En*VgtO7m0G>npV^*mUgLcT=+ z^)CyD6&Tw@Ll(i2dpFAli%j z*S}}*&(idJ!A@1NfZE!;-HVISVQ(pUSFfr>z6TDrS55xddkg+rBIO9ZSkxUgjD`MT zRb49FegjdVk$*dtP1ot{i;YZZV$~&b{gGhrJS9#4u~0}0-cu*khfo*-D}YD|(Yhdm z{FOXk-zLN6IJlOKpacJJ70c;a-+%tI`<#rtDy09kC$LY>FLie7a7YDd@Ls|d zKiRUl%U5&AsS!pldxqIFLGKezMZ`#W zJ0}y5x{l5k8$GfrWB4~(1=0YRYbpNJ{T(M{>ejZ^vL zooMQA9Iw&Q(e3gI_B5Z9!zudg`8-?WFjZ%rmXXf6qkZ)#E#uK_PL=ZC>j;?OWD^-B zCQRJ*7~QjD+kt=_|97q3Hvq|$M6&bX9-FN{N8gXwV_#7qy5v3YZ!)>Vm$x4ZtWQ}x z=nd|?z6*thQGl{)89;|W?o5e{0V18d1Jz<4dD>#`%(=9s1dBRYWR^Z^6fps%wKm$`Qdf7TQB>=5p@3uDlq%!CJ8DVm#9z`x5w&M!v!_aGOuq3!OYDlD;Ctk>VIc; z&Ry2fK8b!>CNK{hZ`1!&q-0@i)=l<~<4;az4ZvNg~YI}>L)+J3YF z3$9ZTLPZZZ&$$CB=F9%ga$>W;`Y<53rUXm~!NFja&BC}R%`Rb+lSUpD%mJ?gw{&nL z{eZFJ|Kedy&MX-2jA+&iQ9UdaS3s#f^2ct&raCe?m3qOgL#8ziZv#%@-@VC-8L%@} zVj5sR!zd0WGb&S^rbg-^5bv$5kftm%@V5s+?Y8VUX>@Q)krRdt6c#u4wzt0#24~~r z>n>ovy7BUbQFO0zW{|zfawNK~2E{}**0nc=6S8lv?k~6g{#{|B zeoG|cXPodHvv!pSWmGr4(MO5rI#MdePvkk~{`YFsi7bwOcQRCCC>3*s0YF)~9BevZ zGVt0(31bwYIZ#Yk?=oXa$r}$;F^ni4zwc;c?i$_yRCc+Ss}A&aZPg%t)RtuTo1AR% zSw+!CoP2&04Hi20^!x*X_wfv>gqNmn|15wV?b$oQqL*{ai5_ zginD4J;?C{DY$b^$;W3G8sjhTw5lf8?35*$WJ5m#h?$8fL~A9jE!gYN{W7f}%dxmn zs;6>RMJ)<}wPe8hxt4AdohFn)+R|X<;Z&-FLq244&gWAL=%z$m1A#~llo_X3b})@U zJqQV?Z;wH9p`Dfi3~IvXYh#nX=H;0 zOJ$Hi@6Rnr)z(uK--iKZvh3JQ8A;Ti0x13 zQcg24@0 zl`@zfRR-;1o?H?^#!y9~j|p(OZ0e+7I|W#_^tggB!PAKASe_A8R zF0Q$;@Q@61PhQw+j7#p##K~#=S3=z^HM^cV&nS+w-ua3aruN=9AcFGgQvU-pdhn%{ zV+e*wA>4Jw3EWT3?dFvhP8WXVSWt)QAs=kKTRVe~Z7`BDoFJ@eu1iQ-UmuW~>a-jk zgfnL1ZNi9u+dK?fzM}3}8@#6oSG$aZDc^3zn-qrbDeiF}k^dLY$b&DZgqj@r*%H!Rs( zh;~-fBaRgT%Y?TROrv=eU2p1v^6M;S%j)Ez;t0X8e`H%V{PKhXJo)Q&Ix5QjS+|=c z6HPO?f|)G8tjv>WI`09iDV0Twi)-D@Lm+L!ZQo&S25#)cs%|Me%<~EByz!6otiiM$MbXUp5LmF64KeBM2 zoNYGE&r>;pwt27;PZ6kqKD4fWPPP!%o53x4QzSVySVPH}6cM&lP0XSbh`D;`e!Xo+ zjA$)dSiyv?)O4^H+-3T_b~->7=`Oxk+{}#oc$H8uF5L9G_&f^SDjeQ+E@dDsk3=hO zd+p7u2&Q&lT_c6KpdgNwT{tO!&)$+L5Y9-$urSTQJXsn>jMc55a6ALw{tC*5D-TRF z9NUB(H{sH6x*co}n_^qzX0aB1B+mqo{qBH-(L^D=I!1P3^7g%z?vmYp+JJCR&fc6- zbo#MbHdh1SyhumRi4|EOt`+L-Z4m! z4xP5ohSKg$4Fw{}QRE-Of&HS=QS3A9389~u75;#{V`caKF4Xo{FI=^DhNMidD|b<@ zRT}+T2=C`uvaa{XWU~snJ<94(ERbPni@ocNT1qlSKSbYAd!L9phDL*z_de{|GQas6 z>pUNP-E=_8hWZF!WL`S894)U(zP>xN)MVR~%L*+fhAD`K^Zs45(GQcz%j||1qSi~A z-Q~htkC4o2IFR)r1E8A+4RPu4YT1E~H?5=HR*G&g71{Hv3h2xFR^V+(Z|F?ahl{4* z))6*y4VI%YU;)20jCF%rk0_`tuKs8yLn=4&K1d^rSkh~S6jF@RVXE*T5l}&9kr?Bt zkA762PJ?w{X6$izsY!QKj%QY!?AqSb);U8Dyr|3@8Y9FTDOO#&BNC_|g}%b5GE=Q| z&g-_*KUIh)>XfTo-5Og(BqFnHY_O}>8fln{@uNDp5qL>KVe;AQY(scuW46N8OpB$5 zaxby6S()MTbDO5S<;eQETlk~HrAs%S4hkd^cCKr?ByBUgIR@6^y53&$+w07?wZ=iL zJ0E&wHAvr;<&yYr24*GZ7#(jDmw|)oEDrYjqMEy1JThV1!yQ@ew9>0xw*>!qSohpyR>hI(}Pv%SjGt_}ABcUi3I$NeOxSgJc+$6Xg| z2u5C>xU7D&3ij7nRUES7@}P?hBj>Eb-3psCGBMmzx67+LTRPHHI>Z}tJt_hwyEzG0 zc{55fs?_4wY5ptim&&#iN-tTBAvCO5(e1#p3u>9X4;Q7lZd|btM{uv^GT)~-xi}x~ zw4=kk>sB;Q>MCdNuD`kS@nf;AmUXsh`#`0L71rJpE5tGO<0K=-)RIQUuU zBj2=Zw(!K(6cK0lo^Kgp4RJpj5RZDvp8NHPkSu$Qa|GMP)LX8Zom@T0a3$Qs;(#wiW9r@%g68fO-3zuEB9v5d!pSS{{ePH$K{{@wg*|$ zq-$%jEgxGdfJP9awq*+07@#wPVu0M#vh$)-Z+&oT(@JAzG`65tf~GG5$Q6k;>3kh( z+Gg_j^x6zxe3*gz;Q5X}z(^DlQZYHW8X1YOSgz$vjj2ec>U_w2wOe+{k@uL52lnb{ zHEL6cen63zo0qkFq+A@-771PkWF{agWKS6CYDuSVa#N({?ReSXfw!Br9;dI1lI-CF zLF^4&w|_G-U?fU4O@X|l7}?(d&~6rTBo4YIiT0;}79E)^h`IPXJD^=aLcIEIi4nBR zH6A+Zuvc(Y#^+139LZv0hZfJP*i)&st-|+EFM&F}nsy3SBQekr%M$Vmyc}jwyk$d8 zK=%BNqIgOf=Dqd!y+(*r`Rl|?4u@b+h89`j9>iEI`3(EF=;_G8T6Zwi{HQ`Q>NIy$%iDKkr>HypWI$;u_t{gIKD zLikB%^$o1$Lg(qFKb9#MrAolV(6e`#cqyF!K*c?@aeqPbY~pPAPfo6blyX{^byU1h z49xL)%ZP}GH)ZLVrDxyjHZd>{H1=B8T{AJx*=rwc0SIY_j=k}i{w6_;cQPgNw3C9$Ca zmXP~adCTJwAt@mVb#GM^gf}M?zHgb3%FcuvqYeV>XI$lC6V0Jk%YhHn%K3c}SaLp} zSyCZh`)+_u`M&!T=WIyu#VC$ixjK38HPW*oSUv(OP^}C#*|boHzbdMwG1O8>3&>_` zw)$i1?pPfIXOg2GDxTb-Ec)1KP4{ctN7wtvL`tivVtxcf7QMywMIx886U6Hg+rz!2 zvaq?l_0#3G;h!HZw-Qja1}79Vkb#Wkf>) zTR5@6u3=?1s|!}r1#&9FQ*=NEvQLAo2DLHe;Fg;c<>3}hTa%r8bR0>1DsgJO(G{G_ zaLIvDq6U9{H`Q0SlTj;SUlPLQyFo^+%|L=B&z7g5D%4w zJ8I&)t&CrGFCq9biCU&mBz}kAZ$dl@MX>K}Wn(7W1A!Surle!z0f%+kp9I?Wpzn_! z6Rh{VsLA9DsjgRb$xpzzS><|J@}_wkT?Pog)<6Jn9@?3Ke+6L$L4*z2W3#p5Pmc2K z+bH8{rQ{V2-)AfuG(kT($1>q8nhQ(`i6*6`TJ7S(k1RCaH#MWpYBzVa%df17WrX*& z%xubbS)HAq!#j^pkZ=ou3`DDDuqS`dWfl}YpuRxd-&qt zk2*ZCNnT;4UQDa9)qOXP`QC~87T+1?iO@`)xvZz3!Fz{#^+2FG9(tD)_5xrO2JkLT zvbd<2T$i~%bO@U2&R(X*$3@z)hJT6{RZB^E%BNS2P;Ue|Ex$Qgz%JsL%IFC_4Z`_6 zQax5aH0^b#PiFp1T{tf0d%w!g6CUcV_PMyhp&E?CZn@t3a{Dt}l0R_N^>JZ6%R@0=GsHhUa!emb_pWjx%jzuwKClG84z}jncyX9}kF#2Mz+y`0qUlMd z<0L{sX;4;q5?rk+Hj{&*04|Fy;vE$fR#boWS>iS$8Q=C(*V|3C)<$*n?qRcAfa~C@ z6tK71kA7L)e%0)LJM!DKZB zjn6k4t^{2pE3c@k$Rn(7TR0jL81Cm>o8Q`={l!JhU(s|QyG`ds-0-bQm1!(&5(whi zx~Q_5@AKg8C~anM0j=w1klSr(sx8EZF37SlL()dfz3sF4%2Q!G(03VkdpK1eEP1d} zLn+c*!*gzJ)n(GS@S9>zK?Ew^%yB*C$#bN4zzmcojiT++RhgIffS(NfTL z7bcwDJleRIC1EWr`z!Wg3DDzDgW8tucEd{3ETJ1TXGpl>h*L;mJlzK9Nms_k3`Z33 z^aprg?#z%FjTE2e3ZtU*(}fJv6F>H{s9q^{^8jiUnay-Qc%@I1uDz777m#Y3I3t-p zvnoD5gBH_D&#IT^jdVp=+OHOHV2FzDAjIYuxsoFQD|S)j?KrftZ(w8dQo4aip|53; zF@*9%X@H5tA|qFsl2$g5J2LE19O4`{tOdj$Fc0Qok@WmiQnLes0p;LueomHQ|0&f+guU=ed*qfRb=2iM)w7%~!xHn5M(uU3cnYYEi(yvE-&v?ckqCcnZOc37SSFbmmzy$>Tj{#cF)rnZYObiqbR`qeBm-i}i?-UB9HVTse%kj&PS^{l2^p+fgG{A# zjXsj!t5C2-`8ZLMOcs>ISn09fjZ04AbB!eo(MQKnm%J{jPyO4SblgAo&c+u(KoMmJ zD}!>+Ysv6IV3T@90pOy? z%n+3-7S&y}vW4SnDlsnmu$pxxUiXc-7nQ``)~qGoN1C|RKy9cs@olepGwxHf1#s|A->&V15MMx^pC4;7?3!3@%7 zoGgO6WUxoF9_vQakPPAdpsGo|VlVG(GaXXBH|gV{dQ=DhXCvAZ7QOJQNp7DGXVU!D zDw92hfl(xdyvcQPnF*amY7gq4HzMG#Vi!az63{5JWrqXXX1Wx1s8B;g>KOB*ENB7I zZ@Q;(c5#k=q0hC8>E5l7V{K!M2gE72c3RkjLRS{Z)4AZm0_v{BhU6@Kye=U&J z>0{4xJ@_1nI(YWiQtwg4(5U&m(e~v0)nky$7VJ958l*7}77M5FsG9|HXJ8gfWCeSw zO+%;Q0l>h8N)10Sk3CzSAGwaIs?{2Duf}AP+|`7UQ}MyEES70#`U3(S91E>Z8ebGx zEi|K}oLoRuR)se-h<{NVV2Q_z3ljMA@$fESk$a8#w)q}N#>K_Sb7#&hKLLgP9tcLq z$fI*dq}`IE%?gVKyMIBa;=ARV=Kb}}I<7d;sD!25JuJ|p7$(?Ht`RGa6vWJmvc9Aa z7Iz9#byPK%9>t!lKkK5~t&z`jxUs6zaaf=pBHZiF$hhdvhs{dC1NvxJeDoPdx26ml z!+ngf4kZ$*i$CZR_ECv61eft;m}Q^|N@i`vbEWd>5H67BtgB^@*wtw~<*bv6QoqGi zWDs$rgRK}_>l0|F7U|43cfgugD$2uy=hBn*NiFeS4TZ<}5uGMWQu|S*{HvF%ir)yu ztlW)vtYzd6#1o$^v7<|RM|HU(C#j-6;; z)7YdD`V0q45nCm~9#YBgHr|0rs@NnhM1Sqg6h3U z#RN_NHbdQ+PK1or4RDrI9X`)jsa_W{HNAbZ0NIG*@(I7K$AA_dy0QJZ+5;l8vsST} z?&l{O<5z@Y0LD5r`jLPj1)mkYwbkXX{jxe}?D%1z|skpGH zbWUNx9MMU`MqdEXz4%dA^#H|!=V{!94kqsgjjZdnF%z^DJj>I*Vv-LaBkRxOo`;zt zcR1EqW5>n$8keG1D?H?P^yPXL`==_a>T)T4oa=GVs$k#jfMkN;o;doFGDkil9Ip~O zq4^ih%uoyK0O}4g#Ipf(@n!>4gX802T^g0XmuPSj4hsyLEjg-}vbMRsxr{t0(g7OH z{kfc}>Jk4wR>6#RUV%OA&=zK2^B*blQzyEH%J#k81SrsSSX$ykOxd2d)ULhJ0A)^A zN@qJ$-EiX00GO+Vx4tt`tMIP2U7QefJ>j>)5z`?~=;vP}u>34_wc z{*eZO$b505%1}Qr2eX+-9L)h~(2pG9$fe5=%_K^!u`Hk~+07&MIWPgWyhYR!AQ z=UDFno~Xw`qEoXRqB*bJ*Dvossh+%$UM1p$ig7K+ib;KN0Eo16Z4cs^sz-c$F0VOg zCC?4D6Ud)L9vH=Gnzu9AJW?turiEU|+aBiCD9h(=^L0m(MP*r(+lSaf8M!r-UgNxn zFS^=(L?ZrE6hRe7?wMv5&&KDA6X#DTZdMHIKfScY`cwuJq3rWjX=&*T59dRwUk*=~1arz0&&gBcv+Zit1q5;f60DJ1Evw4S z&0_K$Uw5+QO626CrsIXjA|Ohjhzav&ST=v^BDLCZ5cOE_dq_L>*knImNhR{_2-c%B z*srg3T7{F9i!QYxV=#Z~j*1cU+ogmsEg(F7=z8Y8rPCEj$a{vYZ9SdI7ukck4JZmd z9aO;&&YZoSK_m*d4_Avky|A@SBaOvvay6qMC!fyONx0-s*X9R*er`QJT9SE?Az5@1 z_xVx%;G|KQo0sFm2`5H;d5hMCQc`B_pxUYpCD_T#%*yUnSi~5E#$n~%pAR7sm;VWi ztUYHQ@nYK#X`2vR!CJIotct4DL5N` zV2d+iOI(lOr{8#(cayvNlTawJ!!vhRSh88O^`o;Yuz*l)LbzS6`0Ig ziF%yrsuxczXJ*oK7z4kF#MMbw3cYqrDp#1c|AwUlIn18xhp)c}s$lF7@+9<)dILVY zyk%8c&yTYpiK>wCddKXS)#`piT9Pfnu-EHsy}Mq^!YwO7+2`YctI<6iIu!F3VHwC` z^MNm7JXK$wB@iY9+)kJ}+O&LbT=RqsA`B^22V}F?c$uF$Ox`O$<$0OtQO!7V)6E=;jxW}{PCDD{9M>md-Burg=0p>$$s1&Sa zL6c}d7(~WKNmBO)m17r6L+V;2LVV~sF6JYi7)T0Z=x{<)zp$y$F{>(22=Zxw;Xc58!$i&#puSRke8nP1H8NK zH=dIlgN0(A&6ld~*KqdNFkeTX=jTcCQ3^fghNG7~W`LuW1-7)O0C_nquNQj_738vR zb2p%l60v1kqER&Y5bFe~jpFyv2}hIhxq*Q|FwLdkP%i_N^gi$Vz7S&u#+QGdYM}VD zih&vby5YL-Yp`PjeOF{gor2PU+3>{xm!j)%dI&hR8S$T{5Z)LqZlM0UX1vYz;z;d- z1FP-Y`f%yJg^5b2NrwpLaE!?VCh6TC#PtzhbP2l*RT~{+u)2e*b=4}P=%D#Qb~WJN zZpN|+$c1?s2jdGMchmU_r-GHz1*m3pV}b4u-H(8dLU(wd`-!hZ>xvEyO7WVFPhn{a zq}LW3fY`ar|2Cj-f+8ANb0L8#4b21^!H+dBDN&4OcMV?iTc(K#6jGJz`X%S1r|8|H zvMQr!bPO!;AGD{XLtm`&Z-F6*kf=!X4zKTX8zep!2(%G9(!R}0KSFF&Senhy0me$E1xKmKUI z&-9>9?-ZCfM7_5PI787|lh8eNidyY--*z1pM-C?P1C?0=!Dk?R+JQ4_B}u=Ij(=X{ z;p7d+RWe!?FEu zxjfz{k~weAs{UCWFb!Q>?zFX0-;bSl{&}J-{++oM6~(YE>J;!`&HRVWcrpZV2jMpd zVeooTizXWE6$dD?xnGQmHL*;#Zy%VUAeT72caN(K8ddsjJ%Kcm!^G5gRTXx~;r@Au z-tyu>##_OH$Mzr=g^?6tO_9;CwIZ;v(fj*l$h-F+WUw(9)MiUkiMGxwvALh%9-peZ z4`E1L#;Jxf3Q`FpvkyXSu+Mw2kJBEe6cPjkK36qXJ?EoR2Na3Z+26Nteg}4guv;qp7-; z!$!nIckbfZzn^gDyJ+^|)m2C!c?RbwcG;RlgEz+HWjXWermMhmd6q>8Ezv+S3E zT=l%q;b1-+RdR^FN;nzHghebD*B`e0xoS%SD=#cm2N_Wpt2@lZ!ij#%>U|^gG!ypS zU0hVf?cm{Ny{al6CaRFk`QYf%`=;Ai1x{N1nNe9FYisl4&&#+Jx|lOYYDz;6ctk^{ z?niw^?F%R@9#+TwP%&G`j0$qr>C4ems6DbraJ8^1b=BrekZm$24>25;x$8Qe(4?c& zz0Lf~uG-9!U$mxoXIHi`Gkz~RYt5Plrltj4QeH_3d8FWSB~`A-fDDo9U~w|t!4Ws< z^w!g@?!gmWGzWtpHwluHtpN6^mtu&~{yC?8$L;fMZSUxb2%2iTa#kM*!^wF&3t|~} zRcvlwGo9|HPI|J${w2G@^6SuQY?wLf?a`O!ZD?<%{MfTu9xag}5z{?vqRr8`Pa<3A z4x-%9UA0fOF`+k&V579Nwn(<@4DkS0w5kfqRNjq~WJJ?$sqNP^k)-7@x@6GdQ1<7U zELn%6KlTiINn7&Q&(lnTtR`YI<#4c+2CYtPIk6myVo$2^FErMDwEtd&8X|>8?TeyAUlwj_P z$$_>h%LOgt*sd%g-WeHT6HyF~(QUz&R!)^Y%g27kD8Ch}E?fI!Q|AOhQLqYz-5iOV z72xQ^WhChr(R=&E?oRpOiP2SF9ubW%i;Xbf_ z!hPMO!?{UZMkLOmNt8r$BrgUOQn6zYI>Hrj4!`>x+JmlWLlX7Fa zk;~2Nn%g^vcYOXxO~Pe%53F`~3KGdI8Iv*zHnbME9!!k@3IHvUP6~^0l-$79F^lOM zX}Wk0vxR`P8;aenv*{8>h%+c5LYfpNSxpmz@lr$w&e{=YX!_N3KcvBqD0?ZM0{Y_yEE?ETkDmdz_zZfRvAlTA1`ZOgj?iEO;Jgk;;T_- z0fnIXL2d@ISdqY>8(VueN`{NCVqst z07_cM=FyGGS6tV3kmEHpHZre#eu?U|=MzfjPR3cvKSqY~*rC(HTh`qPcK*iHrfuKW zs@Dapbk?!}ES1t^y`90)OQg)Vge}A+1i>)g+QCrqirJFVgWN(?L*&No@g7M8&W)X4 zAK7c!eBSJQLg~6wO7JvGtADCUU}5X^Eu0BNVq?jsuWecGH`woFgtvdUh_+>Z_Wl%u zNaNW*T+EEQqL@Qb-FKLJa_;hZ3BMfpln5)z5e{%8<>Iss-`zk)&Q7LbxV|{y`nbf} z-E;DA!W1b38QgQMi)R5OXZeN9@~(y`?ja@x%)^8+)Akx)ml5}UJuyhrn@Yxh#%0;O z?>y8K_%%|1r@{Vsuyt%9s&*|(_|H=qztnZxE|OjDSEEdcOwCIV9@)vq46m!?sP6>H z<<&#kqY&TGs=us+KFq2*U)CCX*4B_3h6lAo8jymRHL8MGwK_JF-RZme$>)!9PSOwG zz&FG>EKm-z8CYAudFJgto9t}$mCKmLgZ$wqv)qMNZGHZr%xNu(X6st9SKC;La5DFHXkc*s} zJ`_wevhxPJBw{qEotc_NO-FZyg&t0YA@U840>$7z`b;GEbDOIzE#h7TM9eu#9KH;l zWo4!q6bDD;oo<;N92|3Ta$bt20^~iiXCr7`qYh*V?ZFA}N%iq`~hjg(d&ZO5y zHTM)FL_t$iPQ(A=ZUVhilD)zVuHPc*M^ueC#CgBf^~CvrYR-DUTtCu4+GbESZ<`BpWQ_2vNv2p=15G1tR1(xG&3DquCNZ%j%YlA z-lPHx`OtkICrV7+Yu5Ri#o{qO;G`Y4#%yWz^j?*-rYbaCCJctD=xYH!&Y-Ij-BQnZ zOu1sNj(y~e(ki_y9}9_aQEpK|Orp4LG>Bcc#z{HV6(_anQqazWx9hZd%bTRP4nu_E zsS5VM|6NKr4?3~AYL)-GEAzwA1=>G8>EJc>%WnhC1v_c6*7n`d+RrNIb5V7>vu>@P zap>aPrFt&Cp)VwgrYR=v7m4qg#Xkw6l0xCMP7F!@2e(D!(U@Baoz&f|E&eEtQx?x za>d1~cO4+?t8_dUZw%^@y`q{L1s2xvh*qB7xGx}hle!$_C6!_Ga`B|g^OB179n8ub z*zU&W(+~tz6y7(U6?mjmon+oJW(ifUXo~3wi>*c&P&J1?QJG_Lru9jF>bdD>&HXko zTs$ShQ&$l9j`!1ueBpkg;fxL|uk$UYZT)+4>qcX386_AveuV`a{1!iO!88;Kr_%-) zCAc!b9t_AM&rM|;BTBpB^5APpr}AvJ6Vmj+<30mnn$-k<&UPju*%=~E^^bsaRjlibWvq@{POh89PX;8#i zm%R;*%Cg;h!Om#Y_^qDQiU$p|}|R*06ed|1|Xd*O&ayviGnbb`ObfE(!3kE+VuM7dOqA`M*H}QyjO46u&N2 zV)-a<4ty-Tbia*ne+j634cH`lcX^FPp=d@9tf+aO)GRMr2$3r@wt?Y5x*lz8eG5M{ ztKf36T}Bk=FvL&}xM1vj|CLs_=ZE}Pn2dj?UIw3ghAR*Sm%D*Vc51va37cI{ap#Yi zQR|01y_I$)YteAq`g!hb?&yoKDYM%m*I9UwfufGz-0D@I+0$VCRdeC>{9SM8kh76( zA_$ap^t$5c_1^#?z&EwB1Ag}x#EFJ#W=$XlYg3JZ(c#%V)7rY!_bQB{kgsuJ__5$#fqS_ zEscfH*Dj2j2$t81Pp^(;w3O?MwOb-_lt2P2SC1k@ijVJF1g~oV7W;S5S3HP;q4bx1 z-tNxqFziT>8uvc-+1tKV`xDr1kM?g?NVW?~r8@?Ng5MDEdPm0(p9s?mb8EoyGQN8a zmYzlyC(8fs&a9OG)WauM^JGZwJkHMkkZpCknnldNeMy$3Ew|YKH4mVJr$FW~f8}lV zz{rQzN-%BA*3`bJ*Q8tnOeN;4dVNhVox_eRtEcS=sd1-6doSZO6AZ z+Dcye|LRUuZCP*pzJh^MUd~pxF6k z#;AXd_Z*mX2^A&t=jg1w>Mt_Y(oqwD*ip#tteQ!))QbrqIu>S%<^NkFHQ%12Q2q^j zK=Y5zx*?$I?1o->O6$Ud_qkh(D=j--05^e|7HmF}87cG| z&|m!SSqmWtLfW`BTFYiazWeUAhUsqXRUB)+5d{}0=P#$5Q9DK?w1F2oG=p@j3axBZ6lT4S9NP* ztfBx~0Mv(M_irran;$4}!!qyacB7AGt-K$%*2^Vrf9L-#QuPh^AKUc9f}bf!W}F zr0i1W*SUmkeBR9#!DluvEgL`3XGeW%WN!9C4Y3=3)AHIsTST3`K5*eP$7dI^66czS5qC0Fj{0Wm&0yyb&8{i?|o^=te5WxrsH#4Rs-+Z;S?|&`pp#H4L`ZgOh6wJQE`bzYP(n zJV}*mFFI1VMpj@AfJ(keclfe-ThVtYkl8$>mE9hrWx1|Ui*J4Azj1B7$7JhdYH8=^ zj_i_s-f}I9V#vUByzKc{r0Rv+g~R$hamAF!iZQzVdBN+Z$Ix}1u&E3vNu=}sdP7ND zm=DuW*q&W?I(WCVjoxxTpmG#1r6Z?g5Dw;5Cy+fX^!M6+T?^1;Qe1HzKl}1L*Ls?B zOo&?qNzEsxra&mHYa{F|HG3qBqKa&yDTI7_4oYTFeO%P!?ba-@ZS8*&6iEAB>U01q zcr`H-qNZ1fEAB9X8bEq!A#H-VuXg`(qN{CdbuSvUyfqMIp~i{>kp}+Ar%A2-Y@$!F z9NJml`H_&>wpo2qNAs_K(gQ|RQ9r;KV< zN@eH>!@1KW%lW|^424{n&X#<#+?btx;vq$*v!t+6%!v0}GA!70)+T>voVcRW9f3;rM=YM(TL zXMI#}Q{gW!h&LIsc|;E9mxy^5E?--g|9Y{GOQ}2d1>I{7O~{l-4nOxH3d{^uFHl^p&ai2#83755uX!GJjd_SaL;V&LqtPJS6N8Y zI!-g41_f&=s`*dOO?9468zCJtvT?cH+7^H2o0sMO8IcK4v#Q(I&c@X0QV`P?_WgVr zJy?3C9&bG(QLA>|+uACTO=_w!apb(U+~fZc~e4M+*|y;cPEFj^@fT zWu8V2$`ib1IDaCepe5E6&TOQoj&8QjJbZR{{o2LMrL5Ziy8gT%3A9ku-Ye=wqH(DT z5@D%ip4CcVaAR~m-TpdSWIHq|e&Ts+exrn`s%rjxNVcUwZduPv8>6L2Tm+Gsgmz)f z3m*O2iR*|!40YWe=X;&XE8udkn<}|7+g@&Gxz4^MC#Y~%Q+eE)e{pBjhq1*!0 zzfYx9^GK>vViY9Vk3;7@vmO#by-4!rynkdE7k4YTu4X$8DYAq3uk#c?$dc+BI*!!y zH}SY^T#PhYin=j7f3Xhfp+W-$IBlp`Zn0&4pyG3xT(>eax^t9?bxYxY<*$^Zeg9sn`ehY_341fTxO1|Ko+`whG(D|&y$>u% zg5P;(-_GUUX4+*OT^cx9LHvXI25eDp=OdYiF+t{6O;+#Bnc0mbBYeyfq{V}0=C@5b zcPr9Y@uxibf;yTzjHAT^G>w^{E?F4@?e8K*bYHoJd5Dt}cemk2uJOr5TZb`Uc4cnF>O?$TEd7g`;Z%2FCi2F9K-oBWJ<>rO6qcYiy;I+v zSVZgPlk$@z7``g{IcC=#-(5E2#q|pgkDHqk%{(6&D9NfjLQi5Gj>ih0yzNP53yr8K zzC(^Jx}3%gJAzIiqEw1!cS+cfU3uKfshbB$MAAQ#fpb=SJ`1)5E5G@kGA!Zptf=Yg zDdq0w<5bd4_1#TV@w^xIue;{f#`xT%_bzM-kF#}Mlj+inm1MQ?O~T$R$>mEtlRP|KZ6zja2HLpvTtT)R4Yl#Podr0Yyo%3Hh2WYsB<{<5 zXA5KFSlRMC>{6W^h%Em6H1cTOTJ(C2xgw3Qa=TvFw7$)Cu=Td1;Mf-R7SHl5lSg%E zXp}*=-n5u({sGLpd~P=$rb_Ebio;$`35Qd%ou-;F%-v8AyL@jqeSmbNn5C%Vtv9Ew zO^`Rn(?B&XJiO|`uswe>j4P{gW;j+V<v4zU|1tKC(Uommzjp-{+qP}nwq3D}iYgV`wr!(gW5rIzw#}P;o_n6V z_u2cj_x-fmY;CnUFlV1*jNX6!-{(E4oA!@ooH+=#2gnAi^({EF^lp?C?#(0h_FQzH zN~ksa)vmrLsU+cO2Hp0(T1 zQn8pxK?pYJ-n){EiqNq(TT9D{s%C>yfqdrmDN_hh)aXWk(ZsGZ@c#n(r}I_~)E~=o z-E-c^_#z3KdN48M5G+d^i%^Ob1w-$=HCz7ppHUGGiPI2)9s&&(u(%U>|Q-9N7VI5*;}yI__8{BeBBL zjxOg(_q5Fcu1>+(igI*NQ~l;pnV<+=;AQBSKF1pKdUbF&!$iq!VkrJikvShh_qM~# ztUoysWTQ+eWLgR3<964c>tJb#L(PJ~Sc%iueJ4f9YylhA!w7G5(^c-9R)>g{mDjO! zRj!Y9wh`1;gI$fMR#!3Iq8c(9SO5n!XWLNkfR6(S{S*lB1a>{ zm&G}so!^tY)ELwrO<+dTuFAfWm#^@XcMNXE^L!=j{7>`6@VOJj;&^b`!&;3)qYWuA zipj}#Z0YvW&f3lMNhn)$!-*DybB6OZj>zR`X3UpguX&^unV8QGHQS{u z0@Gb+eYw`aBeELhr<1OElSFjuKP!7HinX}RNzlI>J56fX@3+>;w*_*dRf-lePc&z9 zOopMa<_S-FA4uZ1sW)u!;`fl0`N@J`)3l*~T#P3*Z~<_1nr%}!>p%eC3D}y-t&5D9FUMDEsi7vizZnal}p+v z?#6E{Y3w#B>)?26sXMRNnB?=0UO5Y3ho%&PKld2!A1>>`Y)&Sh8B25wq?!Budm;?I z#pA{3E_mMj2m|+4EI&oSw(gvPQWf9pcE-L}Jxa!Oii*WgKuJ>2C5!~n!qfhh6armh zj!GHs+stv90vH!g{}T~s*Aj)5X(4R=*>ry5JRn4Aje+0|#I1g2uq@vI$}r_;FgjJL znKjuHb(TcnUnJSOBOyQC1^M=~RYENM$dSlTRu)Rit!AO?Y$c~MR`-b zHlr)yO5ekC>ktY&b*vwzpS?Q)Hg+DBe+RtC=82^r^1d98qM({4U}R+70O({Ujxe|< zw;WOu5I3E6JP#(}1ygE9R{Y}=UnJZ%?n0kwMOlLbz60T1 z9O7T^7kh;89h~-^B^P<5>0e+Og5u8Te9Tu9W9C&93@%(T5+fI+T{gEr{5HaG&a*gB z?|hSqC34f==}G!{B+x~cryxefitN>BbJ1_~O%;oJATJh%6^AF(?PFhlo<8BX!ww_< z2!ZE$IlX5ZyB{Q zOr#zIJrrf!*lL9pz8n4Rw~C{?>{q1JY-{&jdHj`l=eRh{>9ZWVb=7cZ*p@Zl* zgOlZ#Vv7|&>%Z{7Rp+|G6PaafCzOns4mnXlmtouhslSBQ4^5GDH9Q{$IpbR6lJ;ba zbu)^gYt(t+4l=YOm-Md;!Nk>zSakb|Z zBHmfBw<(!}Y$!1{PH!!Uq@d{@89mrTL7qz5%KZobsHpVG@hGNlpG}w7{me^6fg!c$ zun?IKgF3FLm@>j&PPQ$Oz@Lt|O=uA)QMtBgzGm*XVl%IWJ;?q@C>V5Vn9^i-zG_b< zR$uw#mG6kv+(FK;EOb;N-W(`ZCHqIH8A@=|9ZR*M(ryg7Ryh&3Lu(S7!*I}5K+IJv z@?!WbMqnUr+|i%FT>c(;A=WKk+!ADG@xHqj^}CU=SLa3>E3JSTc2TW{|ZaQ+$9UD;FuMP%V!1 zMd#LWYk)Isf?Q-a7KUwu3f5FW2&{DnQM?L&EBH9B!n;5(M?z0`Z_TZ7j_KzRyV%}XQ>}jyIbr~>Mk9bUkW=S&{sJW zth^?Y7a=#aS}_rRQA@^IWi>>{mdMl6p+9BVWTM@h%)K(tBb zn`$l4kVDzSW3`^K9fg#g`^O?}=$Mte!IH7G8XZ1#hkarB!h(ILL0j+u) z(}qR)S!LwpnLLgeZxyEf2V;XnoEcVyelva-7mnsy0`=S)sY|&8enqd!;P(l5IygrE z0ZMVR#TRQve?oQf9B_k{Qp(cLB2DWjc6VfS)g~y`ytxQ#+}L7r-~4?s(t*YQ>;v^p z{MWkma{RPaj^Tl6H9=iQF@?HtN>rjO3(20B_{%jBlhnEUGZJF;AmYTb&$P>s+v18Z zmuZQs=T4G+SMEF23sdVuwqLM~1*K_e3>%K+NOE>lPgmi^#foV6AZl6AKI(N|nDq#U zT;CLU-A_MB@?lHnG<-21LaiFX6N@R*E;=K&UkKpPq9>Ls!Ba#2I0C#QgmHKrEtTypQ;Bq9uC`N*T^bxVPYGi~mJq{BQ+wkq;9_D;Qv4m!M z)xPK;zifPd_|4L~qVmmSUqiVy*{iBBlr+$maZ8MydOrB)$6%jvdOPl%GOa&y^aZP= zBX-uSULW$i#jie-3%mEtyY~P$urvT7)byE?49r7eF#Dc}nD9Vc297JN3YYghgj{6w zgEQ^;-~r=CX=xOlYoN4RP$Lh6rl{>b$^k(#hk44h{jsP25P0!{DO7)ilD(NQPg;VM zVq@w(oHf3^2VaXNz7X6mei{T#R6rO_v?m*30>k-_06!7^`BvB$C-jH3YP7vgiRX-T zjC#8jLj)|VX0KXTCwp5s&Q^?*w)biCm?un8DD>=b7P4d~R~p^*J@GDvR-#-KT3-=t z$4cH!hu~Sx78ZVfAkPK9;VS!)(V8U18c{K_{ErO#yaZK{jKAo1t%m#&vMDh@=#Ul- z=3M*~>(eofp_rqp*a!@-3<@Y6H2-Lf5;W~YNw(seK)#@uwt&@AeF>p%wcimldg3YA zd8A!uEOQErWs|z~svy_NH7e3JZ%#u|dJTmloMAmgaQWu-1Arerc<4p#)2!Al3#ea| zN$OnU-QdR;W)5*i1kV439Ii7%IVJ|tAQIZzjT$nQK3{Mn%0`Zx3UA!5Frbd2ZO?~h zZp&wex;r%yWyJ_+nZnWnbvX2;M@_$aJeANE|JAoAh|G9$FK&5boN?-43g?O0vAHtT z<^2Xg@PPNaO`o)>$Gqpe9=M9yjRP;@UD>$a`n@B_RU1XN3toj_Iwbs4k zfQ~_@YG2i;yQZjNS5-OTQ#q-o<+sxEqteCHMn8s?E%x-`%zO3FG?%Y}ah1tZg&(u^ za_j?>x1o8$oS`hzBT_Ms5iMz{qU&QTI(Dji1o3jf99AVkZw~VwI%y6Qa*)7W*4N=j zUrWyjoQ^SQU{=W2H70Hlryo1urmd^&$w@i_zD})}`WZ57Tl;0&oNi*Dl1{S|woJ&$ zkq>w6cURpc^-IP>JM$hcHsjxliCfzOM&!Xg+vMa$YXGhiG)$lMMMDnfo8<;wQB=*1 zv08O#)kAiND?`;DdV;&eg+S;eN@Sd2!c8nOPT6_gJue$y=EeB?!*SDU`nGDgRjVC; zsbdY)X%Ha(#Sy3q+{~e<*SOAVI{Zo;j{x=kFp?ctfLdW@!25@3uR_B!#dYios>YeOtpbXy_jC zxWcAzL;8}P{F52LTgRh1)E?cY@?H1D4N?BIMWr45zj*SiX=Ie{$cP;wrD8?))dM#f z)bPyLdTRCYn_u6U!5W0mK)7F`GV^<{KxaeTZe^s)LIKYXXIL*W_8ln{p$oEykx2k4 z1+P;_C%2t*C6nc>;;tMuVmGA4>Z~KcX#L6&m2Azx4y1mR^JwBuol9K|vm$-EjRVmM zu@a4GhBgx+ql~G$gj$QqoHg%N=>CJgwU`45l?|W0^M&^>R3vHJ!lLSN#B3M9xB*zC zrb&|la-a1(4kgpB0bXl=`VVUluvBbJrr^o(ONtRR&8Nc~hAsmwX*{i|1ihFzy1HYK z_=(m*2x(^JR3m7j@X!X4U=q$oFU&a#vkX*1we~f3OT$9EnTUE_Ec7rXDaAZ^z;#T8 z0K%}AzvlMU$R2cCKKxO@FrwJFy*M_mG{;JAe$R5!dBE&ch2JUV*-@K4>DAhhQ<#ls zbHe;S1dK8>5?Jv0+DoRjPbHLHp2zWt-j2TL{TqAoPXC&rmGJ=BxqV-QJ_OMm;T zr#^W-UFUZvkZVa_BJo6j((CrW><^A|GsP&0Q>g z*~2q+?BQ7R-P9$&+iZ}vQRT*UIOq4p^gQh$lP3?+OAhd=;YHal%#B|V4$dknzu7+Y zFA=;92NDBhRP!5aFu#8mqHv%<^}*&cyV*8A$wge3e_CM)aR7JmC@IERiYE%b3KKZ-!?%xk4JK83AIYAWSP26ndW>j#pYwHrw? z))GuhE}_1g)0iuc9{J9Yuu@&2rBxj{M$o-qCAEaJHEh0tG^Jxm5b*S8UV(6@fRn#x zY!yDyUjp|b0*-afa5NlJg_R8%`dWHtnb3La+Bm*qq(&=+9Y#idhsedJ@%NhEzEndR z_m_f+v*@v9F}FRhfxL{IiCS^d6Ks|QQMU1#LwyA)6zT+=KW(u9H5!DBu8BiRPA~x= zsk%Js!3*igjA;wI1m2)_C8%~nWDg{K97sQGQdnDxr}-#7Sx>`U+J0xTEDH?8GBEOB z*IROkH;vFBZy|)*OxG{hDZ&E=KEZ!V!Y-d|!zp}GEWr%~ZT>e8{Su|IS*jKvcaiBm z7~aAKDB#*rwASP zh&sPfzlyk6MDkQ{Tv=jrvaGq(MA*sq&+^FhL^G;O*G!JIA{M=?kEb6-=J@+ZqtGx) z4SJi$f%VYI=tEi)dL_R#jR~Nd@yy%rN5#n}vMA}kE5z!5oNl1N!$bx%`U=^xS`X-y{yUF^WH8Dr!(Yd z8sF3PWR=`t?8g{+_A2S%4$6Q!p*ntx?Qiu?eZ;6(9_!mzEAq5p=9hYwsDPkwbor#c z^W?onpBr#@WT2?|_I6xB)8%xbNT@XECVXBcr`@T?m^H)@dzV&K56J5#`;L!Ming%l zDX$|Xmo36rlLPjy>NBjH5}QUavcB))VF!l=Af;cKcAAPm>b>SLS=AYYl++|D3$+@g zh%(7%-p-$j!t#HIL%Vrh@oQTJ7q@$*uBashX9dTbX)EIY;laPFKoFdugr8psu@B2@ zVS<#~DGO_>J{T}03FQWh*2k%Th;U6DrAFBgqKA%{U>Gt6$S;o?FCM4V|0;n>th?SK zPD)iskpETP(7w8pt7b#=E3d+7#mVNw98lTAxbF2SP8_gzerqXPPF)M2MavbmA{9=*9> zRq-A+WVwscc(x|VCV2e6*zOpz2ET=W8a!9gfiqUCM$kK{=Z@R*iwBoy=1Ad)H?1sA zSzEy{iHf;XtiKqF`GtY{#@>1VM$w26_MN|LKnazTk_X2u;&&S!VMK|l#~k8RtM5uA z_Hb3ojxrWjpfI!4EvGULXtXl&fl9TXY?3HTw0>vE+6OQ@@Mv1CvieeV>fbwN785WF&ZFMbnX)H<7 zShyE1c%F3$lx@O;LX^_V26+1XWAjeEUmVL#yFtny^o}1O4>)u1)kxP5V2N~|(t=uU z`4#CNpw_8L(RaI-{4SvMfXiv1kWvW+n0B&=V_HbkoI$ON)fS5k3+9<9)y&qjpi@d& zUipkXj^V#E+H5h+UI(j-W`N^as8~UjBUK-iF0V(gzuL?iT(^o(?k>yIh(F9pG&DKV zqSd?U!ZX3cybRa6C=oW~B6!>jO^fnzFNFiHu0QW}{t?gn;zkkCL`57g^n{BWdH^&) zOwGq2b3P9|fjX+HrW#a6uC6teB}DN!i20kk!R|zoZ(2i%Oc|Y-zr%MV)I3U(PVd;k zKa10+PPFBhalBw!zG=~0Yysi2rEJZ)!O{isEQ5zFvXwRkjQ35-Ca(8~Fi&T_VX<0; zFkRxR@*++{(OgWW*#Hxshhh#N`A53>tiraU8hI7lqIF12>DrXJ8rvAL}QLz;4qVzGG7Zm4L=1tG2&TzxWWgFBy6V`H!xfzYv+E1)@r8>Cyw!Pfbi1PaW^G)a^-!no`yYG? zMwuvO>+h-_>je2tL zT^q6p$~m|qZ;m^!Hab-ada65Ks;j-dY%GFQmDOwv=dsQuzzU~FGx5`}OYlC*@zt;| z?~OiQpHt7%%iK-opp0t82+=XsqFt`>&1*Q+6)hre1C7gpu3Tl0MWp zR+e|sQk)B`kMSXpK6ceaDdj2@`<>}0fvKZQrgRDuuu?!C%E)&eIhtL(G*f_69q_ec zn%N9r+(cbN|3 zX0Q>ZTW<6Yix2TJHqg*NeUO(JK&L#J3YeEtX`$^%E3)Us>fD^C_JpttDo{#t7V&h% zt~E*lW*=IH(Qs`~NnZBrrPB|bM&8Z3i-$5^D8DR3iPvbf=%$x>lK9?Oi25=k^hZ#E zgU8I;rd_qyY93gs4Xhe0@eSj3ZQ{osVpX(c98+x`2n(!o_E6a`1XwiBpk!$_E^%cC z=3Sz3)_7*u=Db_Ip_J$OpHT~}Hs^-z7iIbVsqiwI! zzt2fchRM&3=C`cvPtz=OFS()nsFCiyjE?a{SrO-Z)5)l*dZQd+efOF<*AVm8aIdL4 zqB8}l#0JfF12k_l#757=G7iB?F^{Y-^U*t!dwBX410Q5<45 zyW!`s{4ZZ}$w^dmJ~Ma_a;6n5Mb_SLVw=*o5EpYHhfE+j_=SJ$Vn5S%*Y%{_=g~7V zQBwWuTJdCIvihoR$BA}Uf|ZS`OM2Nc9AE2{{YPMZC3ACCIIRlkry}RiFO@{42x-(! zHkyaaj|wL)A9Ys4XxCnFM$Qqo*ieuTUIw>dRvCQ33I@Y}dVm^{UnH>05ub0XC%+r* zIK@&<8p=au|M)f4s{~vhedw_KPt4`oz9O3k=f$uey%x8 zOgvjIw`s$<@Bv`HU~DLk=QMdQuPP(lv>>vD>@R0ohZrqo$>jk&eA&AJ!d11?(z3#D zCmZpeU7}PXZ&$~!9UJn8g#SA^pS6W3fltn@cCxVE#+~qKu<@xAj^zGTZgRXq{A0uyFEgi#G*!t=MQV=x94fmu}(j*av{?gkTME~u^y zSFbB78*Ue`h-GITO;0TBGfwuB)Yo`utA>(h7CU-8UZ3M<85Nc5+__<@B8}@z04pwUYs_V=%}iw32NN( z;ggK_MN;`Mb?9y2)@20}*#6(s3>ROTJY6!T=>I+vpt!zy)Fko*XdL3$Ay{B?j^Qq_ z5oG)CUUBq9Prznlr84q)Csa0{HL%6%KS@G;3O@%_CWolOHYaw>A0J56dMTrBbBQSm zN$wYbRi1BCl%#3xLEdvOxv6b( z|NY3Ptoaf!FawEqVAI6(*pOy*OiQ7jhe(R7IW@kxSx&T&Wy~6_+1yVZPVIUbu2+tf zuWPE)prl_zB~1K452ALY6;p|wTz9(G0Uyn;Q9EB&BT_*{Ie=?|@m!c2Jm5usyNMsG zxN22a;ZfY5_-X|Cv@55YRzj|uKcGhWlH(FZK}ARPO(RndN>C zZZopL!^|G)^9*t=8sON75D#B~1x7)F$Gt4>--VRSWOU9}O0c0fR$ce-Y zV~A+U7-oJCSmK{gM+t_P_EIQXvMdcIvNa`MK43rb;iGODX*U1|{6C0JO#Z(^bP;(( zt)}NYfxrd8IX`WWTPHT%-DVcSv_!8Hp$OT-Iy@K|5acZ({(n+*BG9Q4gHgTzgVCW7 za%xN(9J(|-l46F4TYYWx=#=|uOTo*A{8r#OO`)`Gdnq|7H*SBIv_0yTr>UZ8&}}vZ zZm;|e?#7wHSG(p$KsR#QI$`f{&k(?WVlQ{ghWnxWV627FV?RDYo^6>hfMZjMEUMkC zbwlg-bCQ*N)p0u!Iuoub3$U{g+@&2~y^GN4)b(`4imnVlK-qOMB5YEKO2vp3;NzHc zGa5{)@K&RYBg)@v2gYun7U_ zpPJe96rKtFtzNLerYWSzf%oFZBfCmiEkL4eHHQ)_GuQ2m#i-AB+A+|tMZ1%&ZZQ9o z%_5)$>FY%tbO6c9=o#!iN@WTXB?7c(4jq^zu1OUC3(WfFXey(eMIkSBoY+pk=u|VS zN5WM@Jn+<_IWUi8+{sm2L@u?Xqf@=Pva6%Ef?#j2ZP+SL#P1!6-8=QliDV!Q?%7#zv484KE&A~ zG%`{s!B76p&VNgb(y94hU{<@f#QWoohec{a_{_4R#vFh`1l{{$IW@h-vfE4r=H5 znq@|3id6v2Cqy$JQtk*_%v1=gHe>buxf+}OAm6(3;sd+fYBTNG&g&)B>+6k%oP5+3 zEZUzqD-!prTF9a&Qb7_m>_m?QoZInlEvp90mfba+8yPxO^o=&SICvQ0vs^BkE__C{ zNVlA(`;x3P0{v9+R0sLY^!MZ{kj{|*kNTOS#D&}Tip|isPGB{Y!ah|mD>_f-t^jaV zuSX?R5tj0{y%0h>^)WuIqL7PMFQ}fWT0I^3Omk)8>}#bVTwV&1-mGP4D*Q;{BK-2l z%JU~W?+d7H>K92zmL#3qxXi1%9>G~G9CA(^yy#shNxbYnbjPnkL-S7N%?ZxPCzg#p zOV#?8@kD_UwF>rskVD_l(n)v-a#ES~X^;(_fXJBX;zCk+kWyttH>+TeWmvLs z(7mrbE(6cq3lLu|ItqkFf14p^bsidV?QPv*!tA3P&NaS`uBws%QsabV<1QBoG ze&!Z!*e2K}IWc?N^uJw|--pc8P_bUt6g2qSO*1pk_=ehxC&aStw_i#3^_1stQPY{m zNlAt9|L!?NVKrSZJh>?e!pBV)K5}ne#kZcQl@I*57b-KNU`2#ehfw%Y&Hilq>jQ^* zb|8C&Y}F2lbw%W2d9fuW#Q8xtyJSO2#YrLree){nosgcGr=T~(yX9?0MgHbQS6C)n z(&-bNr|d88 zbK%qh{xro+npbO)R94q779l?blA-$JwR}x{Aeay-? z?>_6qBT+7nJxeQo2ZxeiGUvp?MxHMI{Be$Jg?jG;pA};T=H;r_tL>D`4lV6kkRvQX zH#g;%a^89rs$W(w#PeX@g=YpQx8UrvB4Sp5MNz&0f9?iXA%Q{l&6hm*kswoiBA|N$ ze!&h_lVsY{Np6_vW1nI?0mll(#^Y5_o|)dgelkn@2kTCFyJNL~NjYX`R(P@`P50$&o; z7ywI!JJWLxCN0U9?LllZkwe34fr>wX`Y>%RObMNO0q?b4m3F284ge!I8G?jlkDThYN9D9ZFvKO zmB4)HqA)|N3WbzUgjbEtd??D~f*5?L|2kYbB|;(cFcCBzEzJ7Vh8;YbSH z)1x!iGYZ5}ncrBlznR)X7s?RKLj`682zPqH!oY+gNP)4R%?#zJs&Zor#i%HsBw;xh z8d`|Um*zy;*U(F2uc>ZYlRdV%WdJK^qsU!6@fopkoVyQ}{fT9#f&4`4L>9#5E4P$? zM>9C1IA`6}!(zNnl^(h~r9ehmX552{m$O-VBv7`|*!b?Iya)EJ355w_j8(&wcXoJn zHA9b-h2pg1C!faU=Kq4|su&q;6|SxKURd;FKznFrUc5 zv6pf+ksuLY;-I>=Ri@iIYE9@rX=L09_&J0tXC{`-RGr&0l`@nRG9hoqF--s}G>8J# zH}?nIfLwd)`v=^RLhT(Dkeir>ks9&t)H^>W1QY@U^taD-v;YrIM%yi($Hk;|fS;FD z4kjiSQ+6n^lSax=t?b%oE+`aOY?q*OiqofnSra_xG_Owp|Ek6rBO zj}v-5-Gu1-X0ONl?=kx%#&GvYzJbrd=-%$1{vgH7u}qnlX{>A-3m|V3Cs&oV?DGEF zZsP}$3+Hb9Xrs2*L8inMlH^sGTNZl^+I_JeZf+dJy;2jCmnM9#tKKWo<(Rl77Y`~w z6H9dxwA5h)!Ial>*4h%ef3gBdV2QPsQcGKhJTGONiWtryls=P>GNInX?6cB^s=QF8l*QaI&r<9~Zb%J;WLb;Yr?3}?9P z{1Ml84B_^CZp6<#lEg|kx14@1Plrn=--*nAEqA(^<$pBvFT3q5#&7y~U*c4O4g{p7 zj1sL+Gt-um*|y!YqHi*3FB^kvDOrn%(ItFp=%}kJ;qeykuVExzKBZ=Q4>$ItK>|V+)u}dt7N@B7eP6N*OcR-O!iPcMBEH&M`8fcfpxG zYqImLS8QpLBQL8Egx3OupRO4u!h&z=RN?bh6ls}(QQ}h|=xwjRnkSei#BDtB_;ref zV;@C+59grwC8opk*~nTujU@r9xf?vyOQWX4L&5d69)qvTqh1kJ49l(W0HXzapJOboAOR%HLf+_K-rdKi1Ao}JKvwJ;96W+R_wowF^G zf{_)e$(lj@+c+uIKBT|taM`+aK{*@5=}PmHt{A@9D8{%Ij~$(2?_ZGbT1PLNyKaCw z*T++I=Ik2=h>?=s80%QFz4uHBX_uze@oRD#3!;p)MDbMS451vsAtTEl14e_)KGv7k z(GicRtlcqN;}n6X0xw~(vmz~2wFi}wAajyPFH(8hMLGLz<6 zU+Fwg0*kT|90kXN_kmdm<8omlssw|@JgdCCNI4hVZoJ!?Eemi7%SLGoTq2oqeHRCr zQG9p9;2V;~ESXZL-81}85;BBXt3~?6(J&9g^m;_Egh=Hl^uN z#X;SUE$(x#mT)IPbu$0~>{TMVFW&~`*N+_isGSvG($c?u@EJ%C1;@6c?tT*5u zVRepFoq#A)sMF*pt3aHc`;|e_x+bTWK?`S5qXC!QoF#8yIL@*tw!ARZ-%`#voNi=& z2ye86#~g;0H>s$p66p5vbInFgV1c3!7!83}JO=UT$ zZ(s|i<0EsbNu$oN`knjs9AP?>W=*?sp-HX|GhBzoQ8YdN)PXen{BOOwT+~;FhUBxN zI^IJl8~(TJR(LVf0|wtwODcLMR6<>&b^c}MzRw0#((Wu3VW7RRHM%y6Ens;VEGm!r!` zU18xWQjGfDdvV4jyS3aM()Fc{&;55Tc7uG_I=1^w4S?coC6$=Pd6zy}cH82&7t6rt zy-;!T-jAu>YG%h4P`1x74fwfVuGIgAXd2Bey_ECo%OvA}3P|(u2$tcWeRvcL>xpLS z42J_Ion7wNkJ`|WnG@!Y3Q7`lHh3AiI6LgWbr^bhwOMP%e z_4_G1t`o{Y>0|uIvppUk_x9$&F|gh1sk+U%sPewduRE75NpRgNRi4ZV4)pbyYU%9r zP%)%mUQ^@E2mUu~;xh*kcWb7o7;Id~_@cI={EZB&%de&L9SXG_#mk-cuIPr!%DN|) ztoE{QCa1R^PJJY#BDG;pK&cKQf{QJjt})pE(M{iZ2#Ek9s>9T4rhT|Oc1(8 z0r5|ncb`iaY2T)^(LBi6PlbW{dj9;Qy2YXmNv-k(mq-*)t&}`&DqP(AYEWu)TB6F&^u9BtVhE3l3zlbG|W*Y4_>a zVA>mT{^9W8`IYwzFg0?H&>Vhs)ky5<=v}w!f9~^sb}IqL#`c^bd*1WEvpR>Soje^@ zj!_T|&yF%gdoIK$;ul2H5V&pKI_iVI{ep%Sj{mo#{x7Hdb4g$h)~5x#A6#1MWW)#H zX;rdHfYAhrk7b{CpGmqidI@alRDr+yjsE4jw`Yf(3UE+P#EJ&IFvw`D7U~{Cv;L@b zBFl9ym=#!&$o~oPr%=M5+O?NQ*i5T`tE9X*nPEp!fX`IR{h0-I2|eDAKm+( zstB21L?gRyW`d1;V``s$*N#io*X;;lStq@69W92=6)l9#n2;31NQicn>!rD2DgSZ2 zKf4FXy2%s#mLmEvtLJvhZY&Zv*d>rZWf?*ley}t3>Bh6&Hnjz%>C7d@-YcVf>K^eS zaxP^4J_cMDjE8P8e!q%mTijM#mY!9j<6z_C(0^g7^QEa&!rHcNNZt$@)NRNzzpY86bq*aO zC{-F7xh<%9r}t?h^yh-wqDH)Q8FaW)uvSq0L*<$RJ1p3Lk!dnUIBb0-d>$gpNyIJQ z@Clx}cuYUY3S08I9Wi9j6A>$Q2_x#sn?fRf#~euz6nL@FlWS2VZ77;&z2x7F7 zX96MIykZdR4hagFPnX`~uEnEQ2?61 z&B@!>-1Q=PFdo}#`1mnLC}^iCrG%!>82naw=+JGAAZfa*2T}l0y)p!IwYaEnA`N5HC*Xow|~F>^lKC zZdAEYvR$~QVvWiGv&`uQEoM5QF!rA_{7>)A&rgK*^R5izWX`a+lH*1kc+vHyIGEfs zQE(AxzZ=Zgr0yof&a6djr-Ul9Tm=(9D9XEQj zB9d)~#m&-(q6jop^vpqFV&qxrIsxE+e!yQq<3P`|8ftrKbNu7;Bc$OV{&~<}5HwP| zb#-Fu9EUck{@bn8$iK`C9!C&>|MMU0Cy~Bb>|}MYPBH$onH#tO6WCNtKsf$;XkUQ; z(whewpidQ@Wo;1L|6i-`N z{?WScB&F{!GMhsi7g*NTkY|aP((sNdnb&6?Z3n?BMbbWVx0}#-5GUvT`AA%PfXS)( zzW43!X>Uo_Gw!(>HNVs5j_?(=>~wVXAQSQ;68%!p#~$&v^8^Gshy}4M2+>~#_&*ny znk4pa1+Y39>Z&%fS47H)5&5)Bn!1)M5XzJ!!Tv@|y(IjJok0B2e7(z^iOK%t$q=v) z{p)Yu1>|D6xlm6Ky?F)`Yc{H=aa|2QK0p0t>>eOEr|8w*4 zuaU3*20f2zP}J4V6M-g50xi@TJH&Dx=ER+`+&kpDOraOj z9OHB+&9M6*pZIvTFd&f|Le9~W6Y=q8ipXmvecbY2g(9wQ%kZq90ZVP!N|qCogQ;mG zD+~HdBJsZk)qE#ud_U#3%xzo>!A9E0&dGK$!LVR@&4>9JSGeca7|$oYH=)x_vNbE% z=db9!V}qSxCqdu#({rhjE4C4yR@_>(X>A33?cH9is*8pa;~Jw zgB4T3>4w0m|2V%DvE3fh*ey$1{z-XLzF8BU8D+eh9nR6NUQD9}4k{;WwG~+vzb)QP zah|3_IBO0*4B%#oD$DO;^nIG%EeKFvO$wHd_XQl1ni8|=Erjiexc6^{r5z!^>q3`IaoSJ!8RXu@FT2}3H2Z7?F@*E$p#x< zVkP5*C-6?(>vBy(fwdZ=7bD_!!O^+~t6nVY;*O>%jiTlEKzqJlK>xJk{P)FX6taf8 zg|Ko%;VMNg2r_OZF~(ra%+DMZBnbs`i?Gz@?N23{D|op8FyPw30QN{3zADO5WK$Rl zV=A0VE<(pc*}IG^Wkh2-Ma2mTJSR9P_3214BuHUqIV|cvV=P1DWb1IUt=vqJ- zBcR|LN>bAp;wK0tP<*R3hLPB4+`&L1C znlTKYJyEkPfG75QyNf%WC5Pjd;-II}PdE6H_h5UiE`_4DTFMqSRhxJehn28s4-E$! zAfwGD2Q3=0H!wWxcgx6`8KY<%kk)??_duoT+G0nDogT1Iw(5$XbcR9lkE@nO;2(X( ziq{+9@Id(2Zo(XEQg!&<=YAcey>dtDwhKLz1Oh3u1QdITn=%whwZu>|X%CCD33IO^|!@)#Gc+o7B_tks<(?XauF0ynISOCLoz1wmtui3Yr zuRVO?02*EDp6lcCo4o~CQESW*6#s+Xj{4md%ID%4t0t5XTRo9N z10|R3GP_90V@NLLP2WP%y!;>yfK$GZ{rr!g0NHM6M!hIqbGOZg=f(pRnp#i$wp$$UZX24(oqpKf|z?t>MK zllCi9b!5SDKimk2-cEw$CcAW|u+vWWE6bajXot%pIIwm^P?G5(v!|LRh>*0{QlqRc z%eA(FqH)8w8-0<;CF_ynfskCjvtUs)@bj(iIM`wR8U-}sX+vA8od%aB-fAn^ zabi}U{iE`8QCvQ)q^u};ZV1lW(iCNfJ}4mBp791saLg%Yga5)YZQEwY?%1|%+vbjKJNa_%ea{)+`Q7*5J;vV8+Uu#Bt5(ffRd*d)F6LpRrp*H( zq*ynQ|Ig0-pY24z``5w+9yva+u9q3bVhc89su`{U41!SXiGN8#M?m9;e}RIyb3Yc- zUYB6@56YGeCELshEc1VeNfuWM3{nCn`0mKp>3#XX_IWd;V97)DTwT`kg7;TY@`S6; zN2vAB!sQfDBJv!17-#gl7yc0VxQ(V|ji8xhY`xkTNj5wC=M5-?hz`{`Arx2Vq3657 zO%ox?!ezE-=~Ij6?YN#bsw@9z)?*6GgH_eht@KiVc8*`Z-9!@-zq|Wmsb|XxmP^~6 zHDxR(uiz%jb6Fj&2UP~?CVn97K)IMHerq;4GBfl5MG{y?`N7H`NAykr;u*kMs80dW zwRNz2XT3%ZtoZ*ir2k`G{;bFZlFiALN~0zA4BXu9o%1zw0ofKW`y$$ghIhL2bu)-=N5A88M`xT95T7)U~=|6U^3{BkCVc`ahlvLhh%H-TShU zTRw@p5JPo%*%|YRu+0ddovxr)x2MLE;!d z0s0lh%(ayN3nqMjyN8SDIu&-hH5$WE@qgcBAgS#p#Gb0C^$;}bq5s#T|L==fm;KSs zX2Wl&1^mBTnIGf^+fR#%5q_&&Wd;xRa92 z2AgK0x>hiq>y;F(ShSxewY02p_`#OlYsiy{k9pvA1~p1BBv$Eb^++#)u>WaQl{DM| z;L&C640xJ)t$b=S4zV<{hcZn_oCjFGv@AQUN2*sE#6P_wQrZPjrIh6@gtuHk~cq(uzkeI%&oAWcV_v%3ivc}-ka?=_b+MI(-TOJajI_` zLyaUA?-ZXw9fhTb>9D=3L%eN{61{d-$x){?VNt>nMgC}`KpXr6z{|<)mcNIqzUE!D z-srixUbL21lC^SuLou|KbUHE7pYXl7>)udK&! z8}Wkpz|PGoea@n0Lt!kfoh6qmJfa<O3S#BLg?< z?DF#R(jpsA*x}+@LtlUuu4;~i#3e(ks*BsceP6NwgD%J-TcpX%se6+x<(A_qd$!ce zVP*TBU6j_c>``fH_iJ%Nxu~UvKvcSpTBae1NaKMi7GBBOId1CP>mBJddc-SM+vV4^ zXjI}kE+of@*G0}@^)k_w(d*Ix!r#g$VTc0|`%krpFCSY!(y*6Pz<%{rs3F8)%y~@v zy^h}|BjKXA1t`WT?nx?C0M?lq$5k_;l%%`ZFr9W48>#Kr$r}9oSWqgb1=gTU6oa}x z<9}hWzoyVOT??DhYaR=z?-UEiRH!l(Fmf|6dapSSPoDu+%^*L-_JxjF z#52hUvq?}K&AAq1C>zbEzVu9JCpa(B8;t3{*U)>hA+wNYt3|2x!d-XLG1s>u9cdrp zNHGpvzrAYs_^C~cF4NiP!j|v>_;S~hsfbq8H&ctL zaP4P6d?G*Gn?^?M zh?s+FVEs?A8el5DI>A7~o}WyS4i@@qZ^NQAQCdeC1JpIAj|XOBG<912Gx$(2-;!m? zlZ;0jeqtmgB`wVoVK3T9?5RUm3eiqf5M9L#fciQJ(d}-t4Qyy4J zd`4U#jRkPjK%Tu_Ep;!q2I(86?Y_0Mfrp z|1#*hee$Ia0{J7X!@Yb9?TShm1+yx3ZRw}6tN~++{`|~Z2esGjY}-+u$~t7@Fvy;; zjRObBektt05dLG%MP;HIhw<2y5@{$F-Q3|e8jLsr9foCe98A(QJG#}el$1dfKuZ;N zI`{*DP!RM4S`H3ejP~p8kF#c-2wc4~inMmOzSqkE*>o1V*&zCzfk*ml_m>Mh?bnE~ z`k%V|ayqB?{SAgpF$9wIRgFy~K}QoK^#iT*fB z&+EPT9^LebsTmNEj<7-FtEyhB<*iVWHu_yJ-se0V8H_kM`)d@61$cW+PhwZ+xje&w z5-T~RYZSuRsZ8gkvl~g|>-O}@LcTp7D#7=r5ydnz>}5$uQ>BS)TwT&vyZVm)HFVb= z8hPMyepRxA^sP_NbcwzzQ?chKT+$Mt^*F^$+p9oE5|@7o9@zve1~(Zap=moMyr8=y z4vRBQG(2wtBfPr=1t zM7AGi(0aUD%=E@x-No-G+BFOYfrAXeT9yRp2vTqm+O7|$#NU7?E5U^ z0)@=k3T9$0;-qA+@1;-l<@qJ&?t$limV=6krGLM&>ncJOwPY!>IZG!CJ`!{uf%|0i zevZJKL|YlsY9{Zb1IC|VT&#^vs0pOpgIFx$nJgLt9xyOlBRv%m5xpN#pix&juWl8L ze;zuxO-4-N6ciKm-$A+R&?eW3*+zCdbdCvK75elxjX zPp}8(++zNmdeX5bX-Bj0eZp)C&U@gcd|t1MIx%GVCx|Ozc9Xnnx~ZxpFXfj?DU`h{~K zS|t{qzavDlWgtzYaD7hnXRrsggY`zj1ZX!H>y_Ic*6m4;P|T6!GZmhtCs=ixNyrk; zg1CcNo+IaI!NURcyxvZe6ad-7halnS0XG^CqL}H|y~*9Q3_Oz2LNflsHQ4O9N0v7S z&^uPqXB_%g3Fn-ZzvR}N$z#6z?squwQPq@0;2x7y+EdNA#(Y1`U)tHA_tuB*;9H}5 zp@?qvM8PpH5F3#Fu~&pk)n{rxdC0{1%!+eZd*%Jb#mftjD_BJkd*o7WS9^W!evk5z zOKGOwI~YddvgGc%9yh`NAdGbS4Q^W$BeXR?Xae{75o6iAbGw3m*WHfo9-!g`awI^H z0n|L`dByg5Hynu5xVyYHH5!3xMTgMM&wxT^QA17?-;Z>6PxKj$bm4=G3`oMC~nW?Pt_*G@!3{BS-z3tr_ zPm$#<4xFbg-O2xA4BUygANR%_POKe06I^C^vklq<{B|h|pn-wY3KJY5nH^aS`aTCbARL#-7{ngCWgMJE^V?!T*?2hVoAb zt(K#dO6+qM_Lf{0>B0TDMy!5sZB-9HS z(eJyD#OxOrw!|-a_3=Oxu>dh+QNcbuZloGQTqn=G)0NqwHdIFtTWVktKXoA1XO7Sb zK+&-%e*z$%*0 z&V4^@&|dyYv`#i+Ab7N9c%TY}_g2+M8$Drtmw61!s!Vn9Eo^{lZG%Bt;R`cE70b1BQeo(XM z1p?I~o|;oy#~|HcBwS~0fDVFJA9XE-4!O~D|1Ql6j>yEVE($sX;~peDi(OTEwmBcz z8K-BnJ^kW7JB=OfJ2U=ojRlV$EUFgquR0D)`R<>~Lrdge(^Xoul}if_CkY9rz$*Uq z|GY0Y`}brzTIW}0+u5xyEz(&D~ z6>de53$m33<12N0lo=pdnOd!-y2G>n(cfs&SPp5;9euFcQbeatqP0_r)5*ApAI!2!)Vn4w-~#LK zvwM}dGEArfkd%fMU7l1j%_5!TkXx&gpJp{2T<2$hI`q!#Heq;}dVmkX`>wbUc8pLG zMv){1h2G3h2G}g_7Jqex9_B5gWjd32 zKu+hiFr&&)cSJxGFa=y|AMVlm%_X`!^r3vnnMas| zX2t!`uxIyGqn#t_?}%g0PqL zJLGR|y13~@>*29Xs0y35iot#!?O%QTG*usqz%S| ziqR&+tbun8+#$WhZd&_mIbKbsLDc}bEQ0-oZFktyAKb2e@?!+RRgX1Z&-$L5iBT6K zLT3iZ(YGq)!idp_2uuZr&oB7O9|yc;Fv<-o{mbS-*S3;H*PdI|*W9hJIdnO3k_Uk- zj-@%vsh+@~!~yjfqyy_$+obJ>`v$O*O8&6=c;vEdpWAzg4bI2(4)~L3d=G<>MOe!+ z_{AZ5IRQ_gngikc{RDP$k{k8+HFhseY9fAM&$R$w-Rl+^*oZ7yeo}pT$sEQLVq8pA zs|YknO!{QM59S2J8g^^Y;V47;Ek9d&#k+0xuFPu5u_;;}-Md++F+=(Px@!PQ=~;tx znD|>NqSQ4^9zt!R6YJc9q@$B=%Yy*ZLJ8a{Tw7XEI=!$?0nFe_ADM}J9@8i+4KQo+ z{0&x4ItY(;L2a9V{&8WUvGyuh=266|7YP)bd2m3Qt^=C}tp>S}(5KnFV&ME!q(Z$( zyjO@yO8=zcO1^4LbO->cg0tqgUvOz53yyZT@LG#Q7>^%jf;?JR5YQ0^zpX)>AAcNq zW{t0!vPAw-8rJ%k7{}$6qaT|ir2P+|)czM&4s{_vCrT$+7-^)FteJ$Mg;zs!q-)wi zP{)Rd}Lop*#GQtkib2hga>1mMUXG(bR#xefx^=qBRSJIjDr{$iLQOz&YvE^-I)SOM2BX%$TI1Y0HX)Q zm?qRY3q3s$aph6lfs&~(_a-V}$-t5#;$u8}ULQ@e+Jv$ap0}ahS?T(Ds2f zG`%<)s|La$L0S?-#f|gdC<|ew9$1JBjK*X<%b*zeRUe;H(!GLY!UwXah54-xF z0PK@E82(72ib=SrxQ$?aw{gH1XOg2L3OZpitPt^6O3cDfH= zuE8a7NOO$$0L}&seZ$m4AX084+W@1It{NB_qL`sxYpynQijZ@E0~RDK&!Lm#Vj21z z`HPdH606PV42%)Tuab-G%ldUpp&RCyGoHe2?V7DMZ1Ccph5)+7^(}50#DJiNiXgwO zkAOZcLJ27smn|niEbn4)b`58svmD%lh1WmNG1o}Huho%@vMiwl9FFZ(c8N9Zw;5jO zN(udVKL0EV$-Q`dj+EcfcWxbVQ>XRpTqpC~kE;N4KLKnX_5zSG-E`|nU~G`JE>IH` zVqu6Z=(4=|M!FosKE6Y{(C3Nj3lt}rwFW4-XVCY>UzgMq@f~wpYk~XY#}t`?X~!)O**z<%jBj#W9lfi^|Vo*n!8c-{s$0!J#FbpX+c~sntZS z%DPVQ#`VcY9DRt4SftlDxJS}pE;E|&pZe%rC?Ll^hCQIlnIq(v6F9 zvU8=qWNy|f=R+e=a`XKhD$bw#s9T8@O?slSLUKg4-|*d>I?t*<`Fk8p`8ffRG`x+} zur)I68kqJqcybxPz~Y>D8;n1!V~L%et40+}ppB?+ec}Fc&JYyu{t=K%L#iHM^1c}Z z@{kaXlX;~97W(#lH9Zi48MHVxLy?0tm0KJ!c4&!DaK2Gx>%8B&$T~|x% z0;+9oo@MhkvP$DR&o|MJ_SD%Kz9%t@SI!#34%hkP+|)1&fL*88PUa8z%UGit5csPY z-!In-Lc!o;Xp7HH=#RH~MT5oLWFIw$CPUG5I>}$(H3sC>_p_`^j}tSS_w&9Ra?7@p zBMD>4-tK`RS$r2(wAx6u zzhy%Xg?Dof9B?i+8f|d5=~UCS-EDap;y@n8^nCxwulsY0{_qLjs_Zvu>>J?j6QtqW zs_$1~hw>?(W+uwlk_BWgF(yfaT}ICObYeFTR|CQ3MczeQHCnFu=K37kWm_J?+XU}f z*UJ{%f_kaoW&ipvOsI!;XR0#g_SI1rbi78b3M-V%~nSoJgm;M`r(rS?qO>`0|$6m_sBtx7ej;{I2$z6xUW3YNSuYOi4MB{Y-85t$$hj7Isv$JzJj z*3z9yjV^jYqz(d8E#qyW8?=JoP#| z;3O-a57#Y_WzoU)ug6(dVA_swSp|*Pch_tsj!sp%_d_v$Zm%YHR1Z7E|B5gDr?LM{ zBr1REx$p;lbm(hJc=B4Z+x*e(HwBGi?euFqH1&0CsAA7cMxiYo7VHT_W`jM{#%I4|&a^pIdjP8_zHH~AgmDL@N zqc~o;M=O~WR3QXO_fPdhMdeNay zVlWLUpKe}yvo%>!ex{FhWCWuBiK3XGjWSIr-p8k{tiI=baF;%@`lJ+1HfQ&dFUh*u z92OvpTF|h+1I{jna9lV}lE4b^x#^!r^(imHh`)g6#~Ef_#`Db|L^+4sYv!2JLiEox z_H>K~$=qvx^+f#K^!at@;}X2gcfMRjh|>lH4l|lmbXa^1eHfA!ta2qMr|V=RVln+%7B)Ojo8`2&HS4S%k^|d8QCW*lBAJ*6RvEP?`3H~bb$p)$I#4)a z>eO+iDjucAW z=f`D-#GiAsH=onK8jCUEHSteAAC@uVJrYcEpY7S*OnSO!HU1i1&zV<7ztM*a`mL<2 zJM0gx-*_!m@Cy9%S_bx&f(y?S5HPPK!??5F#=HjsoK9`0Bh%43Hac1GoP5d*hCvdg zrExa$35AQ9_aBmRLWNxn(i{NQPqLjh>$kCr-FKT@o#0Z}3JCHu!fVUe=%U-0s*T;> z{ax!@yB?}lG8v7giXmP_)rT9XUsW#ugLAP3-Yuu_Ur*htH-j+hplY3u`uJMH9Be^B z=teyZ6^+aEs)gsY6ZC5?l^vc=tG=a#ph8REhoNJH`GzXVsyBzWcm+3Vn#m!ObYG1d{s5bq3 z7x;GKlmLeEYfAtWll$&-Q03E=k5d0YXZKI5Y&7|65uVQl;E`%U%=Wc)Q^)|yagXCx z+rpZ@Hp<%7*%^iKxWtT1Np? z4Tniai1Vh;<+&DJMDSS|L>_5BKKJthxzgIGfQ;OExF`rDAUiFt;L6$>QUOHwl?91{ zk`hc7gr=S2@hn@#X{~VZPnMTc3;~zr9$>f0a9OBngn51bxE_SOj2vT>a zd$W=oE9Priv)GntfU=PLYO9s;FX0q5)%Y2Yw)2^VTHfeWd+UB)cEXfn_s4YG6WNfc z3(rSCt~!+B9{AV8Ew~UNCm){OX81RH7%t5CWnfX=dHYaZj~T421Kw|zJD6g8;>26f(6iytaC)XsZCrussS{r_v|~hP5S088@~5swNy&M zomS6K(wg+@s!7IM&krJ2vDPM^U5z+|v~YecaEFo5sbJJwE-vLu!cXVYx4=0aC97bm zZ#%w`n45dB5CssF{bjHi9rcQn3>!EJe1|x;651QBsM`52-gLprR(g}ORs>vANu*XG zz^;{z!oZL%%oU{{6aHpnI$nCvua=9@@&ACCfNYr>-Ey{!BL$Gw8dZK zw&+~o`6lj%_L;lo5ipI(0j*r~wNB@UL21Q$|+P!RiF%Z4e}xGQtVs08+RAJ zSm0a0q%QrB*b06ZgL~pKwnZ8lP1`L;S>ks)^_$112U-FJ_b<3yZsy;LH2nHi6n>`_ z4B_>JvzCyRL>c{4=FmSn1e;v<#tgz7_*niBe??m*tpr8*ChOG#Ibu(KN@D$O1pu4z z0;s3&TstX(U1!I8Jr#!P%0Ecp_faqe(Z9CrA@4*B)N$O|TZT{wF0R^!Tr3jyWFwP< zzQ-N%+6zqXXod^Eq*<5%cS*L+OJ7}A+GNHpRJ?7c>vn9L+b&JB**%pDM6$6s`J_#E zz8Rg_WHyt3rC_+N4O@Fr;kW_Ah%*mmlyW z`Z*Qt66x7Y4lZ+=C=BhgtggM7%s1|t!M5$AaIR@AI@Ch~J@Ezr0{Ys*UM%i;b>DcY zj9kHYwgaSA#Iq1+BF1UG$-ro*4D7=mp>4f)@fciWBowf`EF)P_KisjQ8^SzD@A9T) z8U(x%O;80xIyv4x-=6lFied0SK4r6hn>Pv_-aquy&tV?~eDA$*imopKvOy0=`;&&V zuzj=&SRPHtR-r&I=JIR~qdhfOkjnIJUr`HP-#D9Jf;Sa(IUpjlGN z1b1CHI$Un3KmDbM5`PC#$+5BW{=Ozf{Hbl<+jUaQ-@pZ&Mz^+|f|HqN^g9`L{)2(| zo^L~TgEC@XURZ6T-H^iP)1MZcRp#a+n*^HFe zccbiZWK4AGa8#&6O-)PlBT&lKXS)*}NGq}&li~`M0QB-X8-=g|@~IAeJ5gB#2o$E= zME3d?agz9$HP%t}dyqJuE|H3G{Su>FtCB>G6w=nX+6|P#oOl=Ok_ULakugDOhxFVZ zU*McF?QB9#KT%=4dgveGSy}@{>y;vVN#1O-s?0HU(k_mqi1bPgIj`ZXQj4tTy@Yy9 zH#K|kakD#^4l>$rF?{8*GV!7`cv;L3M3`BTruGUeM#|^Ohj0Vbijqd9B2&+<9jN>Z zltv~JZcE(TX}2AKk{Pus)i6Lc>rh|>ooE7oP>VP?XDA>RFZlO7 zUn3Ak?2X?W$MBh)zQ$-YScVgno=hFawN(DPdEE#W-+wbRvsx~T&w%cZ^J6R3n+Ibd zPQ$yz{#H4l*n7qC2$FNYk7qA)3+q7@TKrWI!!=~`MkPs05KN? zUnBepE+MckXVBYe4BYg}fA^jMDRl3g&q|TNUWTOHvGsB608NBB2PV;*OV3{m9{EQA zZ|Fqa`cY+h8DmjcOIw=u)PMtlogqN-7cTg|k?}S?Az3*;50@x89(Tn+DzN^XE-U`5HK$Lge zJ#HeAqD6?~e;pf(t7oR5U2d~XyIml(zz7)lObMTN2RgL5qTV0Ao=JJDa}&Sev=>I1KwBJ;q9z<;Npj z7$_e5I{v zans2=`KcYju|b9BaD;aX2fucX+gf*EVp-rN#O_O-q$T zmv|ZJI@d`DX?8ED8xE61P!~l0@lXi-e~x%wq<<|{@v5e|rt7b2LrH}W6`3$47uV+iJZzDhJgzsLI^2cRJ{2^- zx*NcphD`28LowtFXmm6)&>~DChvIBBX!sqGpIJ?Y)2S`C%rPD1U|tTR;P)wZ>+ajj z#bo><6_TUhu^g?*W!kh*?aQK0#m^c%i!#%d{NAw+qoJ2|Y}1@Xs9nxCTcjssM}JSV zEaWpdS0Ru%c~8lzws$!2yy0%MJ4;`jlxx%KNvLS>4O@XxQni&(0Kc#ffmiExxMaAL z9?!#Dq|7%&$5vjKjma{p|sSt3Oawx4m!+$?n3HDI4 z|Cp$cv3!UsFys!D_!9TMWftO9=yhuwY<97Ld-zM&doF<3rgp)M^KSsCNdOVTYYyc1m zO($8m&AM)&CfkGXU)8|r^;)RF!Ev{*W4L9zK@;bF%;=iO= zTOuP=?@1b2kn1|1CmXiDFEVW)S(|j68zdi2OOy4J5p4G=I4=j|HSY!2{KzQr*K7FhAIqpb&c5%|PZ9&_ z*jKbR+~JP`vz6Cyh-Vhbs+uRc5lj?&2TuTA=TPesHojymvH|De1^6(wQTyEBPHKS39_9!2H(- zdvPz%!{bpFubNeb=ap);Ps7IAw^)A!=j7^yQ3ngdO`!_OT#+4#whEP9WTp$EKAfN* zkEMa-DzR7+sUGTEHHgQ;LyYU*(RNzKV_6Yk2?3b6?*8lrhl!eETj6HL-L(V zppHD2)Q56|-uq7yv<41xLw-5rdnBmvXtBxbS-E~47B&QUGCC#7kXw~ZOpIqyQI=E& zgh&r>#8VqyLJ`u9KB%DP`k#BL_R^g)(YQp2m`PRV=hD7P(=j*X7uM~Rl5&=c}1vd5lypT`s4MTxs5Z3z8SAisppSxjLVY&S_@sJ%qMPR&>SJb zNg6G+7eYj*j3ueZxL_Nuum-3z{VrOHd2C3dv?%#ni`B%O5E%G*Pt%Zp#`Mfez{u-X zHHDy?gh7vD@nQr#(r(=Ux(=}0wLrI^IbmR9!7Co8K)G=M6I_EuMWyQo+izy`7U*^) z%1{BR_UyP@3ya};2Maf@6E{>8zTO3>iiuTzUrBM=R{LavdG(3+gP73vkFF-8TV!!pH&~ytS(!D8Ee-ABALTfl zK|gZDRihe|L}*TNix`NYE@3ZcxQdc2zaQcyw$%zJoJuAMbV<*E>5#+0>Qz{&-%Ld< z0eu;A8WN)8fL}TOVqV7YQjmpEjAp$;wo`)xUEfqXYA!?(8j0vBfvBjBy1gs_B}VsG zb)W2N@~=5kgVfwM+HYFV$E)!Ta*$c4oynN$+=y)`iOI;ofqgxE!gZBAefh^1#yyC* zs<0$#rjatU4-QW~i+#ShHgjWjo!{Y|w36(=B3(E0Kd$kOYx=Kf zDb)NKswzM5Q0vYUc9!Cn;*uvP9K;2HHOj_3px zNFovtAAGd1MdbWn+{>J;fC56Nr-D5H=)wUV{U^2A{P#aPqwB|nq_uE;H#4a=v`TIq z!1|lKVftAW#wmoGSMd_5DcM+p13NR$5tSXr>>?aU{GiCDlsY<^7L~Z?(a3x4i0cpVK=fWmGLoBh*CB4jEchyr zpFBNAZh76V4ApXoxfAs720p##10W!+$JSO{^UM)mXbeX?b+fkzJGDBp@(Gw(MMGku zBK=e3cfWbC3pP1;B7GFzaJ0zJYkOrp{zObKeygq&2acR7^6NVK$!-4@tDG4GZk}<( zQpHP(a^}BVC~p)ebws2RtQtwtsB=g2KwAL8P>TjYw36Q=*#%Ef%E1{kv_7H`Xj5|@ zlFcSq4@VjUEaQ@#0lWqq@QawWA6v&$3alPXwyaI1?!pcp7>_CD8C? z(3$F&@bUEMX5x7X5H`SpXI@;gY9^bPnjw|;dV1pYLbQ4Cnr~vXncVE8VG7Vqx@MdS z+Ofbps@9#XjXwDdEt2k^A&S9bZc$+ZnFme!LpUv|PK%a;yh@9!Un6X&{625FwpL#O z?nX0aSXR|M_63`**5L+2A|e7$m~XJwStf9>{du`4K2k89;A7v#rGi|quNqj%5%!!T zvNTR?X3h9(&3vP~s`7i!BTtlWmQ+#a*;sq>W-UzM znV>2#KcaI|VHMkS%GokF}R+uNwL0NE9d?yxKY|8Y`b%A2K%2*#jm^M_}V(8@MovuCFUosfPD#J;}BMp2=Zi=TYQ zE}f4S_0+6Q{em4Tu-v1XUteWM+2CPRAQO}=$h`uK_4tn}Pse-q67PC=B2=LWCb{x$ z;aGuE=VK%O2M!Kw1qL2F)T4lUDe2J67jRow6-)yGMFp@5Oz(=OPYtITO>e@5p4c|f zo+1^KMHu|l5f$P#T0=m3O0{V99qUGZ-^FDOr(0@cij<^;A98pu;3>*iU90@p*xo#J zwUbIuVa=<7{Fa5ncBqx5<+81mg!ptG-E@y;nt-IDB5tR>6)V=qU&yaBe6OCG#2W{@ z2qb2}`j1X(7|Lzh$p6T8znL1lBQ0#F4IcpnVl?-_?O%{?C_sn^JF4NXe3?2-klH?& zM>NC&)3F^#77Y*;Dc4eKdhujoza-t0@m)OT&Z9S4wSGAKRNhft`74oo9f5+0c#_4< zVJ(1--~|T;tYVo%ia96ZN@TKPv;Jx)=aX zv{F(UbZ*~LreyA;*O2x%q`9&}1vAVtRx|MAEB3}PCVERjvzLpWD~)nZEs}DA3dD0& z+>=CyDGvywXlc~mHz4231XCH>IUbVEURs;6gA$DfEeZ4XS6=%0L%eaE+|55f55iYE z1|=*qenU=c<4T3NRx_)ag+RW+MSt!j9_nT*^6=-|%Fm_x`Vv&DcBFBptC&&;{+zduN=W(!rmIu|&-^ASX zzs36EOX{J7UB!NINHykkS_mznKpDfpoicfC1+Bzlty}`=y1zgC77tV`)m!$rezHQm zWxY|KYr@L+p5S2oO;KP`t08=kZT6C4rZ(aqlHsV0&d3BMTaOBuH1!;WFNy&9%E%JQ zwJ^fMwKBW462C!#hI3q(!F;|PrwhYZ+1O0W z4)({Zv#&8?mQ-^S{5Em9_adoNl#!AD4$5G?2A<=AYt;)tZA)~ge85;HmlMG>Ff>HaKkR8U639J*h2U@(CZ zIJP9lS*iEFe_-hdkn3GaIcK0CkiE;y0sBxZ`;7SH|KW$)W&J+49%WV-m)!RzUHN?O zF8Z&pF5uz2?VsO!U|zo(|3}pa_9ZI>QFZP_>k*iD7OHZhOyp$IyxW;+Lm~JTA5Hnn z9`QG;lsmX|G#c?acNl@3G;yvF71G_J%QAW=f11Ug4S3)dm4W%GRV4ehvafnSeq!`c zQ0=dKA^|(1#U)F)_HtwHjNXNY(uDMRxsECouN6+PuJXJpLgv2q8KTQv)CMiX( zrDS%_ot$GgT@ITq6olwU90G!0S;qxuan0&P}55Te5x-#?x2Y z^$0~U9Lj|sNKt*HI3_5MS2H75KzL0L-wel(oig@zLYgjH(!pFJz)2pp5@CZq%GsGU@|V2A1qdoNX3l}oOjqKNvXZZuYf^Jmiae@SeBoQ zz?1t&mu^BQ4fI!8ndUk-B1y^Glom5R5;RnRSg~><+l(dQo_)7b(hoPnj4rP$wGME!XJCvrRYc2zGlT@ z7yt{UCRThIVK@xgIKZdQ46(R)AUd`uQ@>P@FY=vlGyE0aFT{=!3{G8&=!1UIZqly% z*nc@sd|2Am2_ig@xeM1bfj;~1mvnybgW&9UypOkTU!Dk|v>N3$CsL|!EjdrwHzXta ziNxyLUQ`^PrsrWuJn__Eccs^$Lk8HmB^S;MvJiX#rAZ{i9-ZM&`B74QSK3?{nX-S( zCw3ewKlvo$b|>Mo{7R}MMB4>@h6HUh52;EggR3TMyC-P<(7_-LmV3Od1ey9+Ub`D6CGoOMYY-Oy-YE6AN=rhFb;ZQ&UJ`5AhIgD}dQ$ z_E47o@aU@a()f4?`OCm|5!^i!e;RGDUZnG(CL5GrNN{d7(6c}Cg4N$#Zo!MK6^!e? z!Q}qeGWn>}g@*dABY;*2R=0wY#6~t!mGr_>A(4%NxBqZ(Xk1Cp$k>#61)&brM*8!M zOUjz|5+VJQ)JBlbxxkuFawraRbqhdlg`<-~4q?xtNUp~{bWYwjb6-7ZM6Mtwt!;>w z&<|iXkrab=D8W-ytAeF)ds&Oxn0O1yyQhEOd`fR8LsjRzuE)-DSJ7?>xDVkvhRohF z$KMoW@IJlbCsB=P;{T`~TVswNap>B3D*D?%Ze)r4S^+dK0yEYEo8Dd zc$*h*1WH`$rn)-WJeASqMo45ANJ*$6^bk9_qng~t_mCP~a^ldO55JRA zG5%xQ_0t@^gY53rv-|&}>l?!}Te5Itt7F@?osOM!>`v0L?T&3H9ox2T+qUiGrf25N z>ACmZpWpNCy{l@~@_K6(hhr1RD`lp+tj+|v67`h1^YcY)_zY^g{%$OrPAOKU(CUCt zAggG!W1ufp0Lxjfdrx9}Y?H3OZBRN@psWgJ>zP_Qm}t*jNk;C#1u}8m`_hB`8xwWe z@tl?ZR7X0bRT0=pF*(AHZ-pEePjvRGB{yi3t_2*5&t&TKgCbGHtyA$n1 zW~XU!hc_tWy0CYa={&$9m$1-rSm}=^i|KkLoJbro?r;y<#UyUq+>8nPk>%eEa2Jze zYL7y_%Es7Wq-{VEJ*wgQd&tmAx&k&oV-Z<>yQ2hq7YWay_;HdoSjH|Zfvwfv!w{g= zvjx{ksHL?nyF(20T({4W(Kgr^#gTXbdXl$tQI!2@$~ZR`>b_SBPt>$ z#@ps~ehYgcu#z`>Aj4@E%~-evD5g;<&hYAzb59P36f8b!FPOSUR0SUmKsYR;EcY{X zzwqh&dUjR*mXH)%Bxt6+nUqqq#@y|MibJ&Hh-~RQbV^T%9!`3q?EnI@OdsY#;UEF_ zT*->_maz~dip!q4IbdW!MDb$%CVJ5-cL6u2SCg3u!AeJEVpZtN&(eL38I-#D(9Tbj z*)Z50x3dGk9^`r1j_glPVnu|UjB9+Fij+sz=W^obercHWW6#88oIwbKir4vObb0|? z6%BQOxK%i>+P$Z1%p~RZ7xFxr#hs|vT}zYDi(`J_9>r3*yb0F zM5~vm_b0%&pZO~o19gynh;iTo8XyLTyQTG^jlWMNMgk~>UlMes%;3=if6O^EP8PCE zPZ^MLLk9b`@@#uG4Q8@f7x)WW<9HL#P-qMZagN9Hxlq1%cI*y|u`|d8H3E9md;f~m z0MCmfg`a6z6n(uWmNog!%;)XO7y5IB0zTltijQd|_MT?aWlx+~eFUz(&XsYoLB>CulMLG=h2M=|0hmZm0{Z5z{SPeGIfY*b z#uFOqfOLNvY10yKoF{~MKg~=tDu1s^SkTca2|H6%CJi|O`G8#b+L_?lGhF0)(vs94 zZhB1%XPv4wDPcH$-M87^Uv>Z4io{vxCm8#yvVaa0$EitGKQwHc1{AaVH1Dc&4dy*F z4X&Ok#CKmwm8go2mU?_(u&)aB7GfaK;KLOv7;7X6udo!E;f@?P(m&h=7u@LN!)5>L z^KuFQq+ZjmpKcZj_gz|AI!gwM@>dC!k3H$N6tVs|GF;+ht@?~{XcSSAKb-fY=oR|1tCH! zWT*S}-f$Tu)|3c&gE@p}%611)86_ioZ~K|*+uMy&E8VZPCbdo+mz!mmB{$>cy$Lnl|8vrt>x;6mN9D&wiEE0N3$0pA`C5StFq=qpZgxl=iCwW6O^m1zP0jw*J1#_Ap6n>zc*9r8QAXetCvtf`oYqJPTaR6rQ_YALOC}^SEzI z(a>X5)0o(Ga>;}!{URcjG&4EjH0?ZaC|yyK6DqzdtdmhlB>Seq@(E2t{Q-0EVud6$ z8aQ3mV=}UEb@enCGoQFv?^BQW0uon&gFO`;p6wQEBH}6yTO^HtVG{y-2n_Unp+Ap= zZ}u(p%+YtedxOE04t|=*c1WklkcN-59J7Eu8Yl{Y^;R;;Cym_8Tt7}nhhyI^VXF&U z3wH4T;tE&*oueQ<0|aj2TIR5wZ01A&?26Q4hw7|Ieu9i^lK`3)*PPM1hCE_$F;0_( zv$eP_OZnk*9RNJCY*I{TPDMTtI73Slh39u7&M3Cc2&6(XNVK@U@ik@F$%NNQhl7^m zw^9`*cLRJ~(G-5?HDR?^*&`CFqorpHLB!5C_>;i8{ulRkC?0RXu;>Y$%Q|j0 z29}%pUY@YV-^Bn)GolGa_|Qbdl}{HMSA2mM{sZepi0XW{vjAa6(RqwR+U%l@8HI4z z2RkS?XlC%xnv?#~(E`Q07v8t;g$Y5QY}^tryE=CF!};iKgM7pmG~a~RYstu}+qH%@ zif2?cnYV?UuEqEvOE~)eI&?0l!INV0J7~srTwBNMBiCL=zsTl!yHZkdtv-B@tx)$|6n;_ML{Kdcj z&(q$Q5V{K@&c7;=|61<<-LS9o*|$1BfrQ}KfoB4eLC z6#i`Gm*A55q-IQCiZRFAxy^?Ftp`-|)nTA(-GyJrnhMe0e@+F_7&N) zYgKNhY9zo~LKovNY{Bm~TD)f<=P71HGCH+9Y`B*W3+XM(rt0Gixt-U987sefaV9+W z+PC+{*yh_FN>upN(y&e^Q96aT+Q{>@`%7jgg1tSF2Vy20D=Y`(pdLWwZZsv@=*bmI` ztw!gmj%JMpriaSDHL!zaCKc4Y@+RV&>))z2;DPrphpYAi9Wr@0~SX5dYp^ z5k$qrDN-gcc9w6tz(FU1l!&l}3#3?|&hOt-K^O(r@wCai@)tIm`7dm=b4dr6C##6mb3iS=IA457jX@F-IB@kc{eo5Cm2Xbh!zXy@UOAS|pN%bi zC_W3+b=Nz=J4p5G9WU~*H>~bn^QNm7bQ|{VaP3LI`#sfXeCS!Z-8BOJ$qnc1-Yqqz zL}YwQU%_6QFcdZ;e4mlO8Mi)lRlHvcl!_f!%`xyEi|_QNk?ET8UEOqWR2{n-ZmW`W ztfpJ2^uqLdT@x3n8Z1;aBPOG22BL#7$t-rW9z1J`weFJhf%&i$Xs0c-$-}WaOkW z3vIs&O)N$a`4k^MBTQ_gUP}KjykK1>V%~eg)^LRUSw|w7MHFmx z_8$pj&5yf_Ns`<)n_!EkkGix)!)S@rfL#MxEHz&q^RUuF710&c z|Nr(3Pv|nnydH|OMR1DKN0++#-I{rVqD}*+jYT=nqgT+zE*+*b`VW>ewKR*))duYGMpkb7SZW}?eYsL9 z8?P2^LwBt_xzPlv9K14B%r;e3#8OV8U?-Wh8C|jMZ&Ts^Pn>z^t~EKw%g(tl22wdf zmq#4PSD4mxi(rA^10HX51RThGmu20#JWvI z6fSLf?|)>p-TbsF;e7dM!mKxU5w9jTbH2bCU|q5S@`Hh(DanhwbCi=zE946Kh7&QRH4XkNKxySH95Hm-_0p05-D zOQm3|sc&3i%mjW!-C4G`u%6a^Gv|F{z8wU>( zuClH^E`z5bapNW?>JvOfKBs%I^k!I2Ns;ViXx7bn42NxIZhr+i)WlIiKRczdG;IYMQM;D82@vg0& zOc)q$>sRI}S+aTZH>vLkHc`<8D|i!;&HVu#Q2>biKwW z$J}dM2j~0d;MIJVBvnvJO^{x1OeGj#yrnd8(pcDgrJie)@>4+22d^V<Qu_@X88o@VaIm(B!=28?mNE`Gw6frdj<+eX`-cwpg=2My;GQ0P8yhIn z@gylO>oAz$-7HfV$I^2IF|07xvksnX{#GB)(DAxVS#>=z`ax~y=Onu>zBBZEmMBt- z=V_I5?>&ssY^za0!EcZwmLZ|@?>7rxNsnD=a5f2AOcC}4G0Gf zW~LdKKVKWM&UBLCYuJZLWHV>dxGNJ7d3kv7Ev%{)_shQPJ+158e zVLzpb7s;E2+hofl0wKaDfr44@y_yM5b?`KLplAxBot>FC(n{cDL!nb14;-h2X z#MeAu27Sw$LnLK3&h@I@nCq|g1>yRb*~OghJ5D?1moo#mu@%6TZJUTQ_ZOqu<16LgtCENRYltj*>WZm2%-DQf>dScmLchar1 zzJ>V^sa~Pvom@6ByS#E#E~+t*jnOQ9xWZi6QMK+VtG`j8bklYRJN)YwqdLmE6iP~;Gu zM$)okdejczg-+@vE5ikCOe}#1S+S-S%S}O^DA99Uj6TT!9XtO=@X6R{ydNgpntEeM zz%+fTYri38k8QE7mfrP-uxb*iLRQ2#^vt=gEL=oH3}jT5M)wi9ixH*qpZVzw>|m~7 zYT@fF0a?li9Y_puTp7e}cPX{pU z%{2Vr_+W1e%mRM%J=)0)H!%`8IG3vTmUo6{B#>}`QPeu1b@)gCr4N>PE+`tf17G(5 z_96l#{t5DL;o5vArr($rnXmqA9X73{BEMv)&9j8MX#!g=JAT7pYOy~*z}NugM!E9F zS8YS{vHh;^LbIc zdF#{a2H*CweHhY$k#7CXV#oTrTPcWgK}mgS{O#l+jBtY`kd}U7nZXB_7q5<0pvDTW zr?ar@ZAkb7c8NPIOg>IeEtuJSIjC08lt%l_o)N#byy3=s6S!6AH^Z_;W!1NJ3d^XO zcEO)ubzJ;Hb3$8=6M9r|!p1x3FLkR9A4Rrh}{?RF(m)9>`bFhl_aj}Y3-&v>gvINXIJ-04L zij5Mbrd)|AVH)}NW_95XgRv+c?X~l~$-5AY=n$y$e6^Jrw7*~B^}u9We|4$jX|r5L zYjY;WB}RYqd4e0qdG6%Uc42vWZtos$(^7oKs-r3B7*lEB{r#8=uP4dMW4DBjScEYm zVLaf+IGDEHt~$#cn(NBSqag2225>K`5*M%`3=7(Y%Z@O_YqH%6+r?@W3Sv>{7p6<< zeBrX-+`GURS88Gfv!Ka>{k@6~$T(Pyo2MJJ!deEJLX!+({^fe<2D!A7qth3w_Ijjs zcz-9)m-#u9{$7K&M}Wp}joXn)%T13&x)_F;$022HWlLw!n;&WbJ!cOJTD~~NJh$r| zaEX&jRg5-g&G8I0u(voYFKYZmQN7=v!4oykxS(6LIZfAH+xKYzY?(lI;#xlP>`0}Y zl?7QH@aW^uxvp`kuV-w}sU)&}W>HAIv^u%wF1MQcMEF75B4uWV8L@l1wGAfPPj& zPU}%qz0eg>TcYAlDT)>!Z)+#c_C{3@DOu3muX-oyrl}wtr{~YG$hqq%HY}|v;}E{J z94Y%&k0cEC~@vh-iyq?M~I~cl9VknEDFnilY_?2oZME+#tq5Aflj`a(s^qUY>HJOSN4KJ0zR?IBhOH zV_lUHjh7WF8#YhI-*CCss(*amu0%{8R_QVILcl#Z z@oG}WxN{>DnuFB4A*nusNwKG&%GXz^bT;5Aw{TNVbX>fwP1{L(Mf$|&UkY2Pc)=?l#G=}6Ehl`A9^DZ6vBzgmd=RvW@oZ$x<6%@gm>ND0RO_z zQMs2aXxJ(&r0*ypNeKIli(DlWw|V>5qGTsW1{k6Td(~^2ENQgLR6brduzh{BTEN34 z!y6U0>*+((jgE&@W_dXVaVg}mO}kVUNn_?p7wmraI5;+KC)_9uf&Vc&pB|m}NmS>n zIF^0EP_=4z3YBDN%O*2#f4>KeJ-g<}#i#iZYQ5pyElmnq`G7X^>y6xf(y5^T`I(oMZS)N^rv;tYzoGVCzK{0j#g!aEF2{K!F`Og19zXl zLILOke80R>^Fdf-uxiGaf`Sqji}3=l*Hl@zHA52vz$#uAby<}iG+CVO3(gK`G9ImR z90YflFm+(cp-qq>Ap@!t;}bE(<874BplvT8XC~LOe3TqB(h7OwX&b8BY2~=a=|*AJn%DHj_R@ud@OT z<$#IqDR0z|`HfOZm|&V)iiaOD(LqNaWU42RjU!Q23l0wt#Dhf&^bJFn4sRC#DJaie z*7~}ORZ}Jdy65J2xQU5J?xGa38Zk=dD)%(h46k5p)=`M_uh0lUlYTz(fcE1hi3o^Be8m0=*Rft zT<0g(o&M%JasE<)zCMJz`zDAmd5XYSIl&($jEucgNSijyaM=k zRD=BOOuG=EwX||2LMb(zBG4D@0&)P`pI)<^L}(kp_&~@5-ptY@ei4 zW!tU^YMF%OJky1BrBvx#W0zzULyag>5#YCe;NKH^++1==OQn@ri%SyQP!?cGTD1&> z_f6DfUf{th$*N;=-;XC~($!@6SF#L(j|jO-6@C$ZUP{f=u-|`J51Z)^JJwAYX<(EY2iMr0WG5a?LJ$5`K*%ieD zbjT|>mgl^#@G^lqYCImN)bok1 z0%GLJ{kt)QI}&xAJM$BpL2(h(BvBAQ;QecMLq$$-#ki5K*T7X89iCH!<64^2dMlwK zGAgA#x1KPj67es|>6N){b*3AK0vuwaBcl{|MR_pb(&?qF@q6_Oe`K_8{)RrlLWPfF zu|I@NG2_);+nTPfLFk6r%XsJ%Z8k@wEy^Jk&=AY)tU?u$Au`}t4(7g}unLTV9KyfS z$)t0{*0sqYWu#Cq!TPMh2t2uJbfkra863iW#ar|y49-tYJ)Che!;^o) zupy{~E>c8KnER-)_7sPolH-~5Yg(G{u_`MoOWo}U@&B1SDs|V|ndG*+>iXH|ab$_% zJq`Y2n2@NDkcnZ5<4ZSAlU+7ooYY)jZhyJpJe;2@DY@$LXV16H3-JtM#I6qc=DwZ7 zBcx@G1~XrL1QE-gyqnT74B4Q%i2L*sYcSZmqCu@{U_-L;geh*M^k3Dyd^~iT`JvO4 zn9Ht1suPnCC9rcMovYxQ+8hMl;~mM$BaJs7GH*R?eucrh&k93Q?>?^mjN7Zig>sno zgaIq>J5AsqKUks5L&)g?(utgv!eOMYJ7mHoY-1S!?2%i-*EB3yIX-Y8U%I*fn=$zb zQceeS+4Un`!D?*@$Lf6fs$b*srhmLa+wH`91>&uB&iQIEg6%;$6?+DAbcR3iI~yP* zs6QL^&JVC?oN-*v4e-lk1QYytut((N7_B&QzM!6ACO$4CMj>mkJg1-H>#9jr4e3^; zSboxLBUbJ3-|a3NwxL^+8FHV8(p?CMD1?pKBuC#%v zqd^1BgpiAa5H4{av@)B3m@BPego*gXc=a7QTS6U%uoC4T0BI?Zpyw?$VM_!WvC%OD zRH~MvQlxnpd;9A&Sjz&#+A1W`y@4dKKUrsLWl?n%Cehue&Zeth+C~D6P*uh0F14wp zyUv1Nvo__tzB1zO$#aeK3_pwIGv5J@q@PJ8qva$ za3;C;g%tO%UI3`GwG%!&04oS_iv04ftw1*Zs~%DxD@)%qKwC>hMGspzDTnSRK(42I>)6(7;TWTS@Iilujuw^{0Xs3>+cVYM z1+O&|>|jm8mGK%yTvRo{b!(aaLEnO}%$nxyuSUXJPy3uUN0XkxH=Vu)!71;rJ87!K zIx-aHsci8Jb2*6rC&bOM2@>`9wjwtL!F;Pt4hSaaX7Nb1 zHkY5GvfYVkZ2vkO7M6sp(s9afDBTBOt?NUg(3}V=!fTG}Ev4>Gzy=n`OtvO=;&|6^ z%L!}|rU)AF)$f=cSVRT6qOID3E6rDnIYLy-)YFlGxsQY$x*oraqY`7ZTPv7)V#2?J z5ZECcZSC_UMFV~g1R4qnNQEQ>ZAfBKfwQWh+vp#;3f*X=#%j8_A6yuYab{f%BIy0+ zA#-%E-OlR1OA)#h3xN<{WM3aiCt$W3C%w1PJU@Uy%;-r=8yzIi6QpVG&eDibi&$B? zC_&mSXe&5Q_7poq!m#k(qswav_*0`ZeJdsd>3*?yvkg`_lhKCp(=Ivf?(F)lAYYl` zc~Umjw;TQ%M3!d&b11B{J*e7A%#8k2!5aY0>~_cBWVZ7+50!M>1r_JH5t3sHa%zm5 z?C;6>#6B>BX)%GA>8brZZsTDxzuz1&DPb+-lGh1me0XZ=-9@Qk=TG;d=b*pj8q4Re3-J_ySz+en;s7MqpAn*Bs=c@2iU#pT?PRQ?!gb+} z3`)8x@1bi0V!-bWc)eRWhgzwx%d2m)x$TfZ5)3RlfyELh>X3Fl<(&mnBKVmPBzUJzC^Nrzm2Bt0xprimnlOJ zfahkVQou!S5*FPYrK+K6RQD(f>ncF(T-O7INi@&>bc!o)3~=srE8)YEAWNV08+fn1Vz|aqYQGhWx&#`2 zdaavUT59rXc!?EbjeA@h5oV{DFi;Dm{h}+!54el6sh@7FG1a(1(Wjt&KTEN$|HmV8klQLRcj~zRUmo_x{k_@FkbztyZZWAnk zCkyYBM(uFlkcyp5X*hqTk?l4*&tHFW+JqAi9AVOuRLUTL!DU@_ZlAp!usPT&Yc=hp z+RNETOpE0M#jK&`MnJ@?OcgOMMNvpe!K-G+s;q>Jo9y(GkVL9j;14ocqb{^(J}{66 z%c5H$Z&WcNrIotA#Hj1@P@@o!hyn72$4oL zc(}Gc>q`+ep|bu@f%Kvh9|kEd=yHKTW;Plgxa&O`jPU}rM(hJn>2)ll>@Nv_yU}8s z&jj=x(*bB`sdG>pD&QQg*suPmGnf$J46=~cp-SogX`C372DpdEhd3b$n6bzrYPwvZ8yi(R2r3(T`6m{~85byLK4y`sv~3RsX#M9)w))lb zslU#yUD3}iN(-HYLz5g~KD{TsY%4IydJj|mTuz3SC#Axi2GfrgFDl1!rY0NuUUM68 zLch%qUhyKhoG;kA0%zLt(6G9SJy=}YEiZ}<`Nuuq6>l0fdfI5_4#sVV{Rh>bm(8ra z&hIt0S+bz&ic8u@k)v_Tbe~-{*N=t^{gRB+Dy2QR-^%CoFzY!Q6sz{XC_cy~k<`qHk14-Yj} z=_bT$UuCoazV)z{Z&+*6qz=I17?Hj*%nLGRKa$bdBpF&6k^)UF=Zs3bq-=i>}&dCA|{9J*-kx320+1_^&Efb-XdmGtL?!Y;}8*GYcy_L368;RZE zp?z(b5c4Jc#z5))10yRepFyk3s>I*2@u!^zJXi>8e*S0XA+YOCOrwNl6@efF-NyHf z!}C&eGG*4VdGb=M`Lw~BODzA)=LWzQdcD@n(kis4O~t6v$8ozgB|qDbISeZXJ(UDt|feGl+M=wEl-zU7!m5IRSFz(hKZQI_hthg$LLh!<4+)2MB_ z;&+zPEIa4zoF17^jE|QWnKe#_EYx`@2x_V&+ri(nB#DTL4PI7i1U7=5*-2C04w(vn zwT_x)oK;j-BRMr%1OBj4w1oL(@_evRQeB;shgec{E@gK?%vI}OM>=+*09~5}m{2vf z)jm(UxPFoL8s^RPYaZbKso(%ZZ>o10OkzUk-qO>u{JSDFvZ5^Dv)r-1`PrDq^s8vb zd$sp1&f|%fJGX;hbg=&Qs7-0XY4w6{k}-7s9& zR;-ev5Aha)O*Q>^krhImdPirD2ceWDz#G+*o9U==9XP-U<{P%#o# zI~|V);tAyMas3$CK$$Tfjm>5ACDk{CQpu(9;45W44mh!^X~ujekY^;6lyc@+Z1Bmh zDP%woOwiU#&X0kS-nH_t{azl<#d_8E$>8$=rSiU(eV*2~mv4@dxXj5634f+}c_BI* zG&7lMXN2f0NNF+``sZNKPDTUxbu=6@M%r|*e-Kae2fbTAwGIq<*>ZK{oy8mdT3?C9 zlI999fz|axG5W#MJtRs>Zc)lTL*BSs11X%LqsN?>&Y`8elq;p_@GyS{wx~L%tfuT? zNV_&O9(^$-`=eOkxyV+y;gD+!)9#)ry-5&O>k*tK)=1v&-7h2Sem?$w{;7#CM*~jr z&QP%qZ{|0}-8aCk7}_WzJVP_u^PBtN=9S=O9B!5q0}adv#qbVW7Z<--5b7;)H29PQ ze!MPok;&t|M)uEY{YY8g&Cl*qD(evwv>ViibAY1<)KC1Ih z1RyW`0!kJX6a);iWdB(He!gPgU3n()LheZ3N@35b^Go;Q zbQ<0`%zradkHm@>d!C^Rt~~aRB|xs*m6mw57N!NE0Mycc2IZ^cd_B!d`_kHv@aemu%^(O z_k5j~JQGEcPo`pB&xID38j@~u&iqaxjd0-ech>m7Sv;}LuG7FqaIciuRpMP!s8PqBe0Dxux!Ugn~{I`+xwK<*3dZRXYggHrch?!}r*;0;C@DbvxTA_uu z$oNOp{@>YolZfcH=fR5L#zJezFGdfK%l$+zdp^X}+*puVt6VXGe~#;31sQ7{N8%Ftro41{8`{&`ysRTb0JtUNqT6C zZ9NjE$;?IK9Vc>cLqP1n-|Out9^PmMfBqlIUAYT6no%pzkWC#*Qrc8q+c?sN*x%22 z#EH#Z4VLJNMT>20trJzBf;rP~!Deh|t7R|cl6U}SRn^OwQmR^}4jua)n`UkfxQ7Q= z(%gp~=a1ao-!fn)qx+r@aN89%t6(wyn+%-PZSsRmh=fqmkAk!X8Vi z^|9B2*adfeX^MIBY7?Oh$OvAzM~jXU|$PCnXl;+sIl>f7DCOg`tM}!aKG8(h?(S(jloW!iIakq70q2XGuQnD` z4Ra(s%~8|#?tf+HZ$$wBIZEQ~AIrL!H3QDeP4f#n4*R*1T9X1k1{5qvxR;l8a-F3z zD!V_|o7GP!J=VQDfR4{ugJ1!9$Jo5YJU#YD)t7(diVP4SXfg{a0Vjh@Cj*;bF6xR4 z_}Kaif~zbcEe!0FY8pC3RV#t6Sj7^Nnuj(`RS<3if9B@?m?=GlxA5neIEVAJ78S48 zHzd|ImhF0%{%|G!)~t;pAYT^>waiifX;XeP!VUkY6?6F+ru8@f{*OUf2mz45S_q-8 z5B(>t`bjL{Pcys^$mr<(&;M&u0g+p1QdKX~{u3Vq64S$jpdLX|5EPR;7-#nA;b38$ zGs2DoL!ADjEdoGn#D%0zs{InLAQy)PJ%}j@pH|A~vdEC72I`V*W7=*|+H$|Sbt6Qr z^YhQd`u{Ah5rTR&09gb#F%4lwYI$%-Qb{IXnS5tv#-4z_K4G|KfR>p*05U=<2ny41%Bbh9_mb_O7mbf! z2{0W%ng?(j%An|$K`vMUaSikNCX4A-bw^R$3=955#Qp5Ni`4&r3IyIy39RTYQncp6 zdZLtcDuG57NO1m9otgL;df+R~{nLpkg}VxZ*gg3X!^8ay_RE)~{wn{ovw!~({WKfl zrG`hpb#Hlg^+>Baf&s8dPslDHU?nICb&hV{!PQ~-kjU5;uu(HMxs`LFF5Ex!(c+I!V`0t4W_@pNsk>HwH$5!+!}#g-!+vsy-^O>Un^gmFcyupNC`9C z$Kn?-bFW%0n1NL|m zBBl<5Ns{7TdC&SHI2-mbkMd6Y;}0SHbU^)nYSMmeqjfau^#ssA-tMO@QnW`frj7{- z37wstv9PchjsN0syDtT9UsSgp(xvBqKFo1(abZlBV&_QmG%-1{o8VbkU8Sb}OVCHt ze@~4yH4V%1w@z>P)akxjWZx<67Sl*$03u@uNLNu&q9Hu>Yhel4|BMNBy&*tALEB!h zM@&piRHnRSWXAW$QnS9RsGu=lAD2|9e?~3FMmEa9k&zz*eD4hM=UXCJXlPD5;cV+( zA8&Pab(rBIMYqr2%o|o#mX}3DHbZ%&{>>lOh9LX0%ZFuLEjjJjGIsueAwEGK+_rBC zcdi^Wp9O)T5ZZ&n7peSoXa8mN#LSXI%Ysy8-FmS*>$Vzgc<&vQkMIw_uHqbFPfa3#Ctmw{a(e+ z#^$umY{9ycYSsQ!qVqQV-Ns@m-Po7p-}9u8{!P1DtAcXC4jg?yJ^m!e2+_?zRh^Bv zDDpaQC{o*lyQ=7Wme?hkgVlmpQPg!g9UZsU7L zW8^$<`b7yI`ZS4;_(d>`vYIeH7jrX}b{rKOLx%({;i8TNOQ$Ra#GjaoIn21{RQo zjn6CDS*xV-s+0I@IGR~$3?Kw7ChLdd%xZk{nkuu0mY&8Y7$J&BaZPi;J6| zpFc?f<6|2AmqOn6?(7!F@+2LIsArjq!cO{Q)KBrk0;~XiRf0b^hi!U6+bwvAgJ{mcPqiAKm;ld! zBOqW`2OW>#vEJ-_e|st5)B-Ku@O(KtnJ?35cjq7{>OMxeD9g$Mj7VRrF_N{hVekT= zxrJVa`Ud!=cK|oS%;V*(;R%P(Y`)anSB76F$(WQnSsM3;mV(0c%E|~!OiBvWl`=FG zRA;IXF(RY}m_tr$D|X}ATFXQ8`V`SG z_Et1k!%Is`7Z(>35>VN__o#H9LME09-4m^Ahgpq)0{!{kuOh8lufXVJf5J90ao5e#uNzH!K$G z&l<2_1lxv8eW2!JKl?sDX}*P^X32wxf$=oTG_kw8D<$94z|PD2-q7)Oc7L`aP;;4M z#?P_{dHq}57dqbWxDPQ`$(wKm>Hw?yomOMDd<}q2Ats%#hse5u^%;?<|K*2Ls{v`h zn@c(-urvIRa(f~GsG?WOYUhjABPIwjA10dcO55#!fy-@8rgJ&18X#R@DNSd(bJnK? zUSWQ}Z|E?oQ@5URKFUk{{D)ryBb{-c;*dh;<9Wd&CCwR9o0#vh3!XGVrv-7*jWM`< z%Zau!b^oE`gU5Wg$uXxp;a^)F8r2!byeZ6XiH-ONxjs`AqWJ7l+?COB^KRj-!*;_a zVq!rD%4$d)w#l7q#mdvGLO{e{ab&8$7k;g#jRl#$xotFH#?0_~fAaPQyzI74{PMjf zwjm-m7A3bRRT-~gnsONeF)y=9vSEY2+s8#;);+xKK-_^!M@F`!ts(LgGmJ9*ho)psl7poCSIPZmS;n`I!1#O1p7QkCOrY$dncmn{>+7|;Ojk)oV%qE+-YXC$WDpSpus9kHiY^;oGhqo(}0HusD*y{d*i;elk@CwE2&x z^@n!wa`GBi1#RC>=zpn-Y|@`t3aEEGU0r{;GP++7ljfc(@HHNZ+lEUr+kICbu&%qc z>9l%AeB|iDyk2-*F5+aXP7qh|a9?sN%D41Q>tj0bh&!BT3Y@33w9hnk6JdJ|wCrcP z0RQ#ba#(NBo8=%03i9DRFAJ=HxxG_M3?WJl=3$Wfq~o<6PurqoC8P;-Z85#7l&s;f zRTgd3Y%PJf(5jXYNv8Q0iDw<|4#VL&igPK&+?4U96H#yuQCeiBic7j2e^C2}@TPm4 z)t#Y_7O^Av)462sa*c4`f~xad#s{%T{G^lbx;aZU-kXr|9$m){MDRk)1j=voX)9vDNT2Z zF)03kTVW%S`B~HVTO|n+E3Gn`MSAndAoof&VKUG~ZP{dyfR z8ma~%Oq{|;3#5zTJIlt>eyqK@ZI!?hR{M%1!v&s*M0*Iv7IVs8-R~j=6Ba95lPamF zN8pDqVB$Lv{5zINp!fEVX0^Rmh#v*P@jc^mF?eucGj}wf&6ZiJ=H>EBT2WtK>yx}} zAolTiyzyRgJx-C=u)wNj|ISEkQxqeL8_RujIymB0d-_cd4@wcQ<;-q-Oie&bDWX6J zSBZk0pZu2)q3@67#Uw-RyEqPPK34z#hE&wtfU4ykwF^$@o1uV#9rN~+_-KM^fT!#y zfxoSd8-pA!(FZxBM*9yqWTL_XUt!=xm=Pq%T=TPS7C zpINYh&12e&H*RHC*>^DR?-Irv9~8js6RCr~ikmcaE+s>Hddf z+qP}9V|3WDopkJ^W81cE+wP>3bZpx;-rF&lz#1X?%eKx3xS_J@#|K?=AO%-5kkQi^;NHyktcoFwMK{W97Mv$ z*+CJ$zmIeB&0|{IEI5``RDkLj{hmaG4E__isUje=lDL{*-OLty@g7o|=NH!Tz-eL+)D|a;Un+MT*s(?Amc409Z_i=F9^trdFj7lno>h|>+x>jX(-rA4 zsQ`0|@loLGi2J#xl@-x*sK{UAK*oA0jIo;+KO& zlA*jkEjiLn98{U$v`uJ9S@pSg?L$zhslNJ8T4-f}t~4O!U`=;n`M_-c&0(N*1tJTb z$@}3d=zc?x4|jiu5Y!I_xSv%A4Wso2L%0x*%D+L52uPwndm{;ax;OvJ2)z2L2pjwp z@EE8|!5(?+eweW}1@!$5zDrYQsrRoR3`inQieI511O7}&s|%QC5xgp%$7xA@MvRyG zcj@j+8P{TQmg02fnlA6$5oXBn8Xjsv5yJlHMqL^UGP3^7%G9maR4k?=OB+j^w1DdO z`=;E-MCmvqUv4!kRt%P0OQDYfuY(oj)!~eyw8Cv%GvM zo(OCRTyEgrx`NW2&6RSbt~JD`4NtET%&ZY@OXm*&ZY6e|i%F`1hYHwtWTJd)8j0sm z>t_r#H%P~AwBw3Tcr z6ns1r${C*;Ifoiv1}+_*ujHh-C~IMKW42MoMeWcid5b3xk~hyP{(9C!0c7PqMI;st zSnyw@0O-%#h6^FlzQCZq=5p}}?Oi7&HDtfBr_r~H@xAA&jptIsMaEAG)SuEVdu&*? zj1Gz8fZa{b&v_5Z2EqiXq0Z{~?n2}UG$bdo$GOT~fhQOfB_40f+?mM;+~fJt(?qcT zGGs8Ud*a=EoYtU7;mGb0-k2X6xjWw=`LKv0Ra z0OAoejqF&bJu@=igLxFsUa_^ns*?d@C>CnV_X8b$n<5-;u7M4WMzPEv)XakNW{C%V zdZ(U^gtz^#z-kfKuS)bikV|UZ?cCYgIr5{X$uDUdQD^b{5G(^0L`q0#NLpO%1eSvl z!}EH_p3kHEPvVxEIdW?#M7#U?)V`pBSJd5@OH!@dW9kOGm$1*qSGwa9N=DCw(4!Pt zwb;y#A2q!nOQ}A-hdeRLh>mCp(N1ho!p5U;+BZn_qD3k^~M%M1!}@{_A!# z1CA@ko_#QU%zkAX{@9RvU?Q-?C{sQ2=tXmRH%VFiJHYUzL%S=DQ zh0i-)45BFjr%4 zVL2aVh?W!L+u{tWd@D}bc&d4t+olwbf(XU{#`i*r$!WgA-H`KfwdJI|{9Yly7)Vc# zT~A(flA$_7i2ihk;ivFFtK(mWn>HiZ7maJq)TgO%c*8?e74Ic}gj+;$=|`S+{VH1I zUpdvk=jNQAZ%m%l)f5zvu%+a?NdZ)g$~O}Ff%Fm96K01^cV@UXXpj7+p*0=uDTdDz z^&Ptg}*_eXa&Xw>#XUZ&lZAY-4guYmjQtH_f0;{578|AjY z`a>dBm01F=f=;lJOe1VgJ%zWD4oM`TPRY$YdRgDG~M z&X`2AzU2G4-#3EFqyM0oN8MWNOg%;)w|2hIL^D(LSWf_=+##<@C>yQEFg^kvK3aw*I>B= z`@Lll8adLovifmYD;uYaiKt##M})*rZ5oH@*MKh~KF{5pvb?awl;9_yRkYoGX&6yw z2?QGatkH@#wXV!Oq<~+=P$6`4&XXtPK-L?~Fp^*^@mRUM@?lS#rH4oTr`YYe7LsJwvGOk*jPD zJTH(jmhBI7^ym4A0z`llNkISAH9Hx^ROJ0^2qNWpiOAxz;S_DB(JqB`R3Fjfu9wRd ziEeXD9e=rPQTrQ~{x?aK{$q~Zj-3zX?A_8X-p>=>_<+`$?faIJU93Sci@>|rasj;#Hm(tv5AzS8Gba(?SFSihT8kVD&LL>-{vz-^mPR+9vq2{t*j7+n(0WdXULN|+WjH&$%{mm+4 zJVtHt>yz^MX7>r)Kg!^@7FU(dN7JFe9dOz!Ma~1&MZ3xH<>&3Ef)*) zFdPm^yH&nZ@?28%RKMB}%Y~gF`PW>Di1PhO?EWRwGeeU1+VUB3Sj&e?bA_?qF&ihd z8hAIy-2I#a+)Ma!x;v$jIGw1{SG`y4y7L_3rsq{ktHw-{h@|&nJ)bTgDlcfjXiv@7 z{TN5V&9Qn!fOI7xMEjEI`Og8x&kn}9P(xgiW4E1b|hD&Dhe@r&&SPC*aCG@ zbl9wUxk1L-Z*|Iz^9%isvJi@oOKTEbI^lo(a=R>KB;HX_l3g|OYX=6^Wc{bE=<83c zt}DMPue?n5r#jz8U~dvT3l7+BuDEeGP&J4_SOs{ZzN(xx`R=>O5g;4CdZ1Oadc#mr z+47Ym?7IF!IC8y>GioYX;yht6j>s$MOs0q^i3mv>atqwZf|dV|Nt% z;9AVsFWH_!IK4~<3KUQe2q(b2_BJoXQDPv1Q{G7~oUd(24i9I`&u9%twakCX6d3-# zDUHCO`grTF8=p)+%|l&icGqG7Ehr%cm&Xls*IUJ>O7l~_+TE;Ht>XvH?bUOk1oC?s z)Q`p~Cl1+9@H75%?E!}oEY*h)$;Rv>UB5vc;<-e%2yp+%lQ-59GJ_|URaXmG6!P)u zDN(?`7;|i!^hT+$N5`N@cnu$|2F>{;uZy8-*3gs$^gDCQ7VQQ_5WbCxD_MP$b=13bazMc$va=TDq4O*Cg8}bjn z{h#^KaWJVb!% z63aEjzuBc}=OB^%>C9gGfA1XI>ntjCpih82#7`FueI>Azl+mAk-Aov%!^FJsY`>6d z@B{&sbH>&W&6Aqka) z+2(7FEt>nVY?w0&0H>RFv90Jf>)a9D+ZcOrATs1JucZy(1l#)Dj zZc3=70g|<07JC8LN|Gy94IFIjxbduvu$`;`Jp7V6!L39Ms4HY5Vq)R$q-r|`h4+~v z8GxMzhRKW3#o_6`^G9lxiTlI4>UFAXHoG61r@|63gDu{q<0P%D>9`eFKgST7>(Fxe zC!zm!dDY+kfLcPPv7mV946aNOLH*;W4|5AnqclP@cmzzkq)mV)^(zCskC$@Syh&5= z?MkE-^nr-K^W~%>S3uVENaf*S)mB16qDFmNpbF8@r+;cimXY)lHeu}UzQ*VM>3Vmo zgo%yq5BP9j<0Q%+8jQu*nBV^HJ-EhB{%URcka7 z?dRt=Lg=$Lk2HuHL};|fc0OsvFg*(R2~MaN6nq1{)KXRi{SRxY{RS0kw6hcBXdw2o zPn#?jejYqn*t)dTx|G^_G(8y}Zz_!U`*)l{mFns$B@`sNm0{RHQh4aa#RWMJ_y3Wk zYmcA;>vk@~?VWT}^4mmU%^1mF#8Yin0m(84O+?Q>*SdnTflD4AALDY`+-YPE zn9WvKGpNV9Iyw2i0asXJ0qdEZuQYANA!8kRT~HJ6?(QB=X7RdR5l#3vKxXX#oVbl< z>(z3VimicaJDjZ;ATu8!qA;KLQ@v?S3E$OW5GC*weXEvr7cFmZZ#lVW%g2yhXuI@Q zx9goSRLTvvU3hcg6Nf0@unu5e&$XZpfZ2wEf}$VI#JE&FGdBnJGG1piGBY!C_k*8P zv)Z5(%5@Xqqxbdo0SqW1F+zTj11ON60F$zn5?q$3!C_xoAyjBYv0@$=7?=r;DFC`( zm*9#7co`fA5e89+iUQOYtE`ySZY5as<)}8Hqv_z&ezJenwy)VnRbQ?2VlKJi51DnBD0>DKc)eJ7^N5b z?U8jt=7q}+=(yzwJ?tphA1xms1OEYP2bsMN9A_cHDbK{`ElK zNV#aftWZa=MCn4L`iNaZJKy`r)#7l`7m>{{u(6>Li1PCC?4Ws7g?<33Y1f7fw)C1j zRp;?+`rGwt(=%nT_32QBsFjjb4&e^adp9x(AA#snvLEpHD+=pO0?`o@;^9?(qcPyg zbrqUGNJTgs0j3UYIrP1n-R>Cv%Mu4zjlP0m4RIWvM%KN-iM-uc3O0#an!Y`g(apQ- zppG(raU&*&SGxX&-S#G5yoG<@Y1fl_;e#$HWKoC~OKNqmv9y5%%<~36rL!wQIHp;00G4+} zdHItr7p~hr>e|k05Zt#wEp5^(mc~&yRY_Zd`oOn8c|K>G-%C+v9;5q%s z#Ylpc>trgQj^h1-7A^SAR?gwG`%i!F-uQ%+^31c(K1CexMu7z#*DE_{yt)2xCowJ6 z<2?rvCvA6$bv!4}rmuDkxV6G}nrBjRqP2)V0!&)+{Bo8R)*=`ZvcY5wDmSA>@n+fh z7L5)2luJ3^B2!o8vFQ+%(VgvbaAUt&pf-^{$ zptMkN?B=%_a-L8o;TVWXWix@h|Kc$MieR3fj~0Z@n#*xLwhh?~ z2WcN}QsBJ!e{7T)BMYcHzNb<)K_0t#7>feOap)4mr7X_AI8-`Mr>IuCgN1xwH2!r- zFapAR&d}k#tPc17|ED7xAqZz(`K44meB_o$T+0x(dE$Rq)f%{G>~#i>q-d}!?qey5AW8O}u=KtxM#$0v&q z6l2l4j899)EAc43BZ@E}^daN=*)}x9_7jC$>Q^$^h@{@r`Y3rt1+o+t^Kf`lBo#-t z_k%+uQe39_e!gfHSRp);K=+H7MtuBt+N^UI4K$-jOrM7cVd0w8N454q#MYqy5Ze|s z8JB-HT1h3Eymu{aY81Cs4-T!zm=fYDH`!H=)}pT#t2W=Qmre=xHe1j$ww#2zSZa=e zGgC*qCaY~uL#7m?0Ea+|+6w8)UlLL>V9&J(uy^Wte`B4LB5*euv6FKe)%TcRmh(X- ztPYj@e>#F0EbD5imB3F;TcWnusJOsIhENrJF)?m!$g8%W^c1f)Zxm>UP;<4JkNSU2 zpGTf$OFPpIno~6?;YFaH7@s$HxPHgVb&_tgtVK@2%nnE=C##hG`vMHR>8F(*@Sz}S zmU{9ML?w&eVRn>hc0ACD(@!k^-```T$@M&mBvBc3J2ZY+9CC5mk8sr?FLF}8x<>c7 z`v35>>J+0p&Wry^uSK3~3FlPH>zW>MYLW4{#;B3Opw%OQR0NMe^mj@Fq{%Jl!1|q< zu${{!EV2tGtv7lB$H|q~^WTo}#` zw38d5+`l5W-99)NUA^KR!?~prqx)Y!CpMHX>&%|=T4^2@M z!j}SGUp=_Xs{7{CMVv*-(EM`ks+UaZi~*f@-XvZpnn;_YIAvI~esdP%Qy!s7+23(v z5IP3o^S7^NCs}-TFK_@Ke919}7bsNi9@JUWs4f%Be)AU7oov>d~7Ji1Fam9EH!jk zHTj6DuK(p9WNM&a3-bxEr~FC}PwE0~J(08}rN%a~98BR_@r3)oP<9sAUkT7L5KvwCm{6Z@*qx{2 zvD7w;!3_DQC=OP(m(FQb>B9pQW$8czm%lbIjiJ5=I*fh$4 znCXi@U%0>G6eph-YO{B7&#J*3N|dgsruG9eaU*+38hA{+MFx=j&<@1>^6I^Fb9^8|@9eC*#n(FpK~HLlOSjz5U_A!N05|WBGWP z&mBUPdG*o4m^FWlTQyBg1oh1?uf3;>pc~ByYgmb*O_~meA?7qzYZ*n1#lP+0-q>W& zk4cKC$)<1havE@hO_tg=TI>u4oq{l^E)n5Tzo;c|MG*KQ6CnIzszKag-hVdH)COi^-UTSB_}QzAtwkRELO-R16=tT$02st*AQ~ z%Jqjjv_r6+Uki++FT+H|K3>Gz)i}9SuEM~ZYZ}LMJZ>Y%EBerT;&0?Co9gJ zxOXe4pKM-3QfSp||GaD#>@@b?O7@*N^0gzWs%QwJN!<{Z{+nvt5r}zXG-pu@f13YZ?8by+mLAbiEy4hlASY|qU+!Hsf(n5GSj=B` zlT4%#4DK7KqI90*wvTR8bKZ!=qz+L4NgdQ0yXfTLv*lAwsOx{^HQVbIJY=JU61LWuI z4;UF$O!TpoCXO5y$ZQkG(sP0XX)BBgY8$v+r!HyYA~ zrfWOZ2jyRVqEzGby0~7Z>HUs>&o~`gP%CiFP3c-8-O<#J(EYLFkqsFH`dvy)F&RC zn$6727U$RjI>5ZESf7EWm|O3s*(LwhYHPLAHMKdIaI7dJo&IIkN!N0+mbT%DeON+E z*zCCJZeq&%BS(+kPoLWd?q#bL8GF2uIwM{{2VAT1dNP@OQK>UDu{zFLU@+l)P}W<) z0AW%|5Pijl%O<#uHph#$LvL21LvP2)wpyE#PUT@}v%%?#g|_hM+Goo%-Ba|6er}Uq zb<&hFL^+R1h7}*OX6!h#B%I94zDU-I0zDL8R>@t1OFcnJuJKYmMH6;lYF;eDjU|HO zOp=gLt*M_QP1LO;VOmwPpKjBXls24XzDPn)L|{lDP=2_C!12QZQb#?ady>~CpfYV5 z2+vlZJLUpY6ChcA)&U^sdzJRPn;_;8P{4seUx0{frNM|QLEVC{sb^1KBs{ErF@hD6 z)y&_0Vl5D}>JKN^y=vYc>?F9F4o}3TJ2B5wab3w$g;?#YXpB}INKjo0L%roxKD?q~$$UGF@XG@gV4)~D{#Q` z_#n@}K$t&TWsEuQc@at;$5TT@QHKQE@WjugQ#~^S2JHyGPB2@8z-JVT40{Tk9d>Gd z+AzPW4)`hw{_nVuK*mX9Wl{CW;1I7_p4^x~7jF6)+d@8D3fARVJOv0#;rA-*_`V&% z{reBqU?H|9;pI-V&z`PW4vQ7aZ@E=!G1Z{{;R2zEP_H@-%C|E%Gy@qzcJ_hQ-vULu1zotA7Io;m0jx9+6p;Xs!s$zmKaU zgBW}V#Vl*t?7UpivLQVCesXeQbB4tP$o$O?5^G7Uzv4<86bx(#(q{A9UY7aC$Z82m zNQrw<%UX-_3HFb}{Q<{`^oO`)OzX|&Ho21ITc*XLf++%SK`d^h@;@&}2o4r+{>I#Y zMU_V08zy?-F~NG>+GUb{ zXm1POlar|c%5Du^Id$wD4`urX7dw_0$F`Al?f!0#mng1wRG~DggRs0hKZ+<|kkzhW zL{-B&sRyUV+5S+Mq8J0_kh`yW1-CUD#Cw&QVOpfno2Nl09@&h;`WpW`ll?w1M6BhlWnPWlQI?ABb&huOKX z2utv<_dQB8y)(0~DEnptw^jAp@r98IIqwFJ6|VS4WV-3*jz!WIO?)!iewYERcM_kD zv%&-Q+_x_o9)cgI7x(Q`jE8Dn^>`JzZ#G-(cI_40zh)J8Q8pGFOVVL% zsw>Ji-J1WP`W74j5B)gWJH33APcLSCq@xbTK?ZCEh~yy7k!UmD6H#Ws z%v>ieKZ*-1z#NIqqQH`T z-EL^=(GSB(#;m>pujp-&{INI#&tg?WC=j)3{d1o$cI{pgJk>PwY zcN<|BpV?e?x;ELUVsk|tDHu^XWD7z^rR-Br_wETc)pfj68fJcF0fo%%oe|}!w~O~S zHs;Se2C3Xk^Zm;|dgOAyQEhrL;XJ*n7M+G`f`^roC1@Q3ShR16B@#%B!FvxXD0dWYBG zl>U%~C3!WrIe9b{xi4FB3qD{OYPYk-9K=dS?7evc05pLF%pB#DJ?b7fAZ)Nu_g)r!W>4Juvv=#^|>lG)ACQ8!*e+O;v#dY$R zAS(kiMgcwxHgj17h)n2h2C)$QGNlC5f#nK>o=Un=FuLHJQw zvmY6GqU9kYLjsXsSwxF1E(v+mb6}}f!-G5;`?HB3Z{KaI!37e;1`nGhPuMCUW-A}f zQ{ImTVTyCI`g%C!2cO1(TIuDSdE~V6(}0`eUCr`J6~=~}C%DUw-StjmPqP$lM}zli zrmyqr$+|ILU$H$1_g!K8m_~tamVea6OJrI(;^i0sPJ0Xv>`U$A5FZ#Q1r8Q5KWq(S|YElgE@ zKusna23htkF3}%bNd`xgQPqCFIK-!i@mdA1v#v9FBuHCkG0vYmpbJG2w83v6?9qzD z7Z24(tL#+%S=JY83TZ_kiUf6qW)hMtF5r|4Cy&Tlf;)!=V>L0*;)E1TW)K29oPH` zQN(?H1Id{|PNghZ(&8vm)1hiwwlmqX^g^z=;ltTJ-n9xll^rjive?zvR=QnPf)h&K(;IEi}fES}=2a2?lYJ z^@~=kKx5Wj;hT!bH>P>c(xKKMTT6(<=CcWOtJD2PhWU2EZ|TUiLE0Sm9sZLxL)q8G zpmQR1J*5EsKoloVsH<7wzSo5xDxLC>QLCok>q+E_J-rm7I`8@?@Tim7CuWdNe`qvXZk8#nN*aGly(q@t)>E zA?qb%Xr2`x4z68N*%zn3gA>d2b%??8#jx=#oSXdkiA5R{Jayu$N5f@TsNp968UHYB zu}5hiCu%?VSEOu-OX#B}z*w-ugrS4&ASg83E-^N;IQsq4iE9{+@J4 z%-&z)hwDe9mLg-Eevv z4Ae5nXAH7D-lhZ)aD_9Wc0y1tVfDe>j$_x~)YJ!(I}2Tl)#1YuFNCKsA-?*q8F?su zpM6s7;|mP!tinUV(chiTKAc+Ok`K2I>PPuKbF7&!UTF1gwUv?&8SR!VmS@Mo#qgqMPG2+v^w>DLLr^E66f=u-?f%2fQ zKg%Q^5|&&sc~Bq|E5qaT+h$mBcT2~6re^R#PY)QH(95LeGwv`n9OinD1fp@Eh1=Wa zM*+I(7NTeV!Ro7q1F^ymOs&Jy6S>T)&3Uq>NI4@m2{jG_FRcyycc34%&G6pBqs7Q% zK=@O9KF49CyI9$8S2}6=_7dvI1Ca`ME!~~>ec!KK?cVl}7jnR}@C7{N5w36!>W=k3 ztM-#*2##LY8CBNw=zBhscLbZ89dJ-^Nchy902fzlJ(l_{=329EzHhXVG9)i9tLL;S z1_b<4c#MqZe;Z;h{dP&~b>o-$vCtj!d82fP^4)VO zYcd-r?g~pVe$)I7y^aoh7e1eWFUXtZ54OhvtH=JmEBaxh)RvFUzkl``RxvJOm=~Fs znE~OjppG2*4~6)HCM+cY;Z&)krVan|tz9WAur9`#4bdPo<%=lc3Xeg0-^^Zqe|$K% z#H()_RVfYamBc0JX`PZT$c!ut)UtbM3TqP_uJ>t5Ur?@T4x=_!S(&Pmd!&RwsD+AAhAJ0XAl z$=caA3=J*o;*chkO7+pR&z&7GvXAZ(E%;TaY27wAHaYIzIp|76pSOUeP-5(tN-ZKF zm2ZlU>4qq#$4_sF1ApEw#4)6TH1a+6_a;m4BcMl)C%1xlQ~PtsXIo5`t`SxA)>aoE zw=gQ}UFU2G7_K4_kE(9+&;7c=QNVg-t)WwY+ubR=e7}vh7Nc&oen|1*`TV9ejW3e7 ztUK^yQx^LWtLQ~p1MHwc!Vk49d7OQ$CH&cz2g40?2+JSewG4y-*FGfG#i8IzhLf5x zJCjLCcK11MSZpPpDY-0COV;R$*5nzaTG$RHrHqZH(g=KRI9lSvSx*I{WO2>w=G`@# zK@fr`0I=1pl|o5c+{grO1G5BRu1V0vr9~=7kKhCD=VlG;b0DSy69y{qlZ+Cb{=aj}`*vvu99*^k znCp49l19ms9W)CWeEL{xe6Zt?`!T(vh1^8^bMfQw9bC&XUnj2U!wt-?^m>-;E|cA^ z&d#*i(%d^ldiX1UH4x=6=Jr(6l+eRhE-MRWnERV8S6_a@RR}qetJ#HR6NH5VAC&k+ zYcVU*-8&Z@nBb1uK041b!qRwn_fN|Kf89VbDGQ=naBgEm__|!jL17Y+yUa9ek3ltR zspq?arX24jRxU$AW=-86U5A6xD1(8+i8RDJroauTJm}R-9H(dBo|yd7jfR(2`E%kG zi{*xmd8#Osm#uYyS>t=5B*6dxwKW;)3hPXmZx@y?01c684qEfJ+;VSI`<%o7Ad}s9 z&KJZBxh9UtR}$_&1{w`uppt4PB65&9h#O)FNS_xqz5(Q19?A(`0hUwUd2^O~Xa3TK z^eaUSew0Ao?TI?{mav?76V5s|e9N5UOX9p6Za&q;L^2e}U&Iv--{uNg+lt9%O|xL> zS5O0ibi07OcIsa75^Q3IBwjaHD3&;t&ZKLx;$Be8M56CS2dhir2)}sbRtA38Fh*SG z?o+}QizRPd)hRaB@K3hKbTxrSra*z_lD+B=mo2L2kBDLi1AfM8$s`^ic|+*%y^Swj`_BgqhOJ#@O_)N1nGbx; zy0wuVV1E_dT}C2?Q33fjL}esmYNn~#t@Qj}leqRQI`Tzo>a^yu`PcIkGAAZs0WL#G z8F`OqCNaiIBu_Xyn4CQSLqW0aS|KB#^QMV3T-x!Kt(L}%YtQ27P>JYqxEGaNG`joX ztVsnrlYECW#FuJS1%}S+J@8iQv5AiEr+XX`T``sQ>ts%~X~M8StL-8)$Sc+}XIQ5d ziMtqwCe>pP&#LZYln5IhK!cNn@2L?VWSm8}5bgh$+5!+EB*?ak{?G;QBbshy_$(>2(K0Gi(5|!)lL~>`&@X2cav3o=@me|Hv-U(5#2ik*? zxA4zn|6-=8&P9ud)?z-{K@8mM4n0z6??Dt+)+nGDh~V7TX6T;VFJx6jTcN>0#o?q2 zt3T(!alZKb{gN_dLBmV>m-LGpCxYiv96RW!;>;P5b}#LJWgXq(gux&kC%^?-7`c2yWuI8Qod#ql40t8 zq$m6JtL0YmcsVOw{9Vw1ft`gSM2aT65=}MGUVE0;&QybTj-(5T0WL|1R=b?m1mJGe zw(65Lqa4}^s*N@3UhATxXmCfvZ#C!#_f?f{k_+Xgp zj!ciUuBIlE@wDH`Byd1R14r}(h<=%84wu)*ll3Og8lUpj^8nS*h^(~D^^czcL z`t|hzJ6JtoU3N|h>^R}*aDvqUCn!<5xgK$csC!C4bSRHyW*s<)bG^~he1&0*F|NVX z6J8KvnG_>ene3pY%{8!n0D&i7R-~0tEF-rU1cj+Bx}ijk zFxKGFchCSzu1-bk6&A%wd{Lb|3I)=y)f$Pe{fLcexw@du%=r~*HG7PPu-0uDqly|6 zS8DkvPGj145of6rOCcQYZKo0a2}^_vdNB^OFpcAqp}mZ6;?Z5)tX(5ozSC9$fwIk{ zrdy!Vk~ZUwB^qdvcGyfC*YGZi2i#R4@L+=d;cY1=(r9(V-}cftXP4jTAlXKrP1Kcu zF1V92PZyWd>?5WNX`v2*JdQa2CNXC%4*_ntRriObVdOfML@XIh#$fin#21r_B9*tG zb74S(IB_~SieJV29SxpQIWpE$Zue@ zAGjJlO1cAMPapFeFQo;)WnZ2?Ky-0O^>)R(F=NmFN!lv|5u!Ia4^t;|hy~>IsN>Z8 z!RG^D*l~8JnCzOe_H~8Sg;{i;fT^b?CImT%4V+yr^rbl>e-7gPm6s}XEj-5qcg!@h zB&lWglRF{X#czQM69&EXbFiyUS<@Tnrl)J3-+k30J;uXfcL`E6`}VO$(5_M5$Bl-4d^$v z$kYVk6nMU%Z7kYKg`6uUK)pp3KhI(;StdMLb#b9ndcqaRg7W}vsw8Ni7r2vc2<~}X zOrK}uMc;7~(#4{gMR=!Ao~GTyh>5mIOHL_-`~ZEXPGO-gkMi?Ua-rcq@oQrvNHg?* z*6(eQqL-B-?r9RN8rxH)dR~^QG#h!|GOe?Zvcht)O@?j4*+z7Y6Oq%)i(Y3U|LKnv zQcHA+R|s<>n)Cd&kpH7WnQBFiL62x^Nl(y-&zxybb^l}6gIcFS)+-8xLl$~`5OwR> z%bD{#V1EQ%s5A zW;ArOy%X;Ljf>2)3!1IW5**D_yTeg_ZlzT?krO9Y@ zGk(!-VHF?*-pHc5KAf^@=OqrQ+3#5=F@h`0HZCw=PtQi{c~!bXGmgiGSn3(oN}g4Q$RSEyvjm5mDqoc8g1Sr<{%Geml3;^u9~vaDiYzi%Yu^Q~E!aHF zx(#e``m?smfFmI!z6-e~YLME)#gYBD$gqJRSFn64G_IcZDZ`$eu&9(3vNbt~1n)?b z|G0ARI_u9E?q~dBQDV6;iDX!&RAuN6Kg_=7wS5${F{q#qMR1XQyF9%1poqC&_=YWt zdfFa|Hew^1)ydKc0oO0veiROeJR+TWRNKRbqy7w9W!D;F5>$wy+oYR#(W_ak&OwZZ zA^e<$gH@v^0tl!wxmTHi--VziMcD$jrwu>s1+wFYQ)HmyK41D7+P-5m$`ey5!EkSg zCQOF>$d-*AF7DMI4?|eg<2mee6`|RmHz5bE0Vd^;6KkN%ymH9F_v$A2gusgo5D=^< zsv6h}e0==Mp|5Ml4E*A^=o_W4$7{*UgD=^GyePSZ^T4QcX4(RST_eXB-&!|b+XD^M zC$~C_+H3tw)tStBD=H zAte|l@I}84ywmDNduh)tte*>;Uv8Vs9>yX-C z+#a%WBs%p)NS<|y2k*{o)#&234@!qu-0YKUuI{?o0zCow586Pg|r1Aq(&VnI$-|_KKX) zpsv|AI-9O{i=hK~deHI6r_igvoqTmo z4#7DL$@qAfSRD=>%Cm6j%+Ci&ekV@5ucG>fgPbEDKi6sDIb26XM=~tbYbjYjB+xO% zzFQOk`y1;fEa&GGd!Q$0HCT3L_de)L+H zkiLEpcJL8wuuufQ?I`1xe``0+Uft$d1Id<^K@%2#>8m80IOSbrgYQUYNDif~ZI$^Z zqm7%tr{=1zTK-c$Anm1?u9+fvR^t=JBeZg2j;~FUhC;`&x`j7coEJI6#`{dL1x@-@ zp8x%h78Z}Q*JO8g^Akk@H;VD&=~qKQSgEEGgqn@d*=me=0H5t^hJ2^f-AsIZDe@nD zUX8RAHP#b$xL?hrM#UKf{PTf*_1VQL`*zM(KKi%NBtV-d7ynP2hX95;1Fe zKZ+q5uu{){Z}OLwwf~Q*Z;TFXTe^*H+h)h;*tTsO9ox3uaniAE+qTuQ_0sn{x6ggQ z_ZWNcRZFvK&Z@efe2t3pf^<8{&T0TEzOLvKC0%O>;rzZeXxrkB&);S~J!}#;Z=ZP`+Qc*b&VPfvT8|KcrLe){n+<}z^Rtxn6;j9s zPTi6L1Y-11VlA^v@?XLrt43;WsM(mmpMb{%V((iIv+NW&Gz%+GmaQ21e(O9Ggc!L2 z79?2-_<@yc?Mz5p&I;`OhoSt3QPx}d^Py}7?WTs+P3}zPmJS8P zY+UT~Ib)q$I_ghhLQ=J9IjXD>PlX>J6wfYv3|NYxSw6Wvl?owbf?xk+kdCjrxKgwp zq>k5F;_bIiH!7;ySqPqFv1W8f{nY_gt=po0%)0I}wg@zM)K%)*zVe%x9={WeO&KM4 zY{KPVHu?FUQ|otmlbX)~ZH$UZIQUeDTdlF#^~zaS5aB$w#Y4%{@6O7l){3*241^=< z2n#5ZhRLu>HW^r*Pr3C_G%SpnrLcBj&jqX~pG}sh%SsvaouRBXI=X)%{j z+R@XY@=aPL++{%Jj~-puRY|LQ=h%i!56(?KcFlQ?VTCtI8N%^O$w}rAD~Fg6tH>6u zXi21(4E)a<-Im)XBK18P_10^r z&LixD%`|n{cTS~3S#751w+}VRhGx@;J_`hrDd#nlcKKfhHa%4*$#RVES?^Bu{i z8Zbr+M@?0eD9h2EuBXEj%5g}5)1ibD)7htUy7UclmK)K5H2`8IGtaJVJ@w{920~MJ zo_y7dhsv?VJ#M71MCMR3qENJpWW^>AuAG;q991mhd*E!fb+9w45pVuHqZF4GC$4TG z0IU98L8attXR4}cgwDvK9=LNi{-Q}OjYfaSqGwiYO*iX%a9ZuIYC$Bki=^$1`GgGR*tk6*3f6k9vSVP~VX5q6VtsOdRYFDi*l26$!~($Q z*jn6!W}{<`k1dH{+tf%c_Bbr=x+pdfr~3^}E9rfYCaHkZrhY%VK)+b;P5V(;$xexx zTL;>e-u%NhE*)0w7+1GD5a}lgVm)V(R2_9NMdg6@&>C3N0v2OgIdwMo>?kKlUhNN% z0_JB9qE~)K!Qpo>t$;zr%@f;YIw@ow9^P2>uddMAz?mh~|6+7>ke{Wnk!fw~h&ose zPAy$87abhx{lk2AkPRkdM^Ra}*{vjSbP+gkqGHA(_Srd0sb^;F8yqI8sEe1SqhzRV zpew-A0jl3A$cz@8bz0_UELSxPyPeaNMax!!k||DpiO-HvIYD9MshYj!@@+pKM2o@ZpifyZnolOro!V`V zem^j6$m4;l+Mvyki*3P(7@3I2e+5IB;Yf@;&Cpa!e#(vQSZGKq65@nLZjc&lM5djb zadqzJqQieaLW{0Ky7fGCxcuHw5*jW)dsKSFH`h>bZ5cf1bdNY#y148ySSYu?cGt2} z#fqq}pev?I50$Sii;HCW1?Funa$;4VgzUh3y!#_;&72DQZfi#h3H0k z4`9^6Vc5){mUIdxq7k#kfN_syf`16sxjJRra?w+S+cUv`g&h2`gYOeMiVV1NY14Yi zCvKm{g}FF^fIIEBlT}Q2r&wL|vfuGN{@ly<e@Z z*4U?IP8z^295`r>y0;!7vdjS_LkRN8ly*`8vt^I#*!euyOL&|F$tO&;VLRTRQKOq4 z_9lpi5E#aN8@iQa;@KEDsOAw7j%xIXhL7Mwx*wKGKQXO;kg;<-4;By7_AI>5)o;nesP3FH&0R{7cmi~AA;YMjxpgFa*ONgo$RCjGwcQDM-_6+vs_f1O5kZ+Zg zBw=Y_Tc3J*6h**w4HEChjOY0`Gu&L^lzMA|99jDlpsQv)^{Aef9(?7rM)NKkJd5`UmQjRldZvhoL zYfYQVPm@LZ1H2hMjUU(I(e9oPyH1RnV;%WA7|;~Kccz{cZgzl@_H2>~jwD7tJ>(_P$H z(k@56^HMN2J7>R+gvf(C9pv3@h{?jZyrrE_KRG z4<3K(CS+QfAi||#jE63zf?#9os6HT8Q{~%2z~_N{=fY1WD@&aw^H_ zjn&Rf5YL$CoMwT^(p2ja9ytlNYUn$Zc(9!bDL;@$dgV9l+8icc*{GB`*k}_a}^@9gi-AsMShm zDO@|3T1pgw51}IE0w!25AVeJy6EqXBpd<{ju!37M-JB7eg(+$^=%J2cmvU8qTU(zE z!KxMO4Phj7ZXe z<(Xw97qprXfB@K_BrMX(nS;8f{-;2d@)^{N=1agA^e&y)BB4uDKr*r@`7}g=3X_$} zHp{QB>K5*v{U;+zql{FIK3teCT<@de)zcAS+Ybm+by7sC6g|n>O;$l%lzsWd96H2c z^I9pRYh9jGHpwUX7q@UUq*XTfOFZ9MMwPb}^9@yR<`iepfFTCJ+U41*48%IXm7+h{eVQ{;sQjE)H)ifLzZH59%gEL_qXZOdoyu5FP zh=Yp{(SB2#WKH0podssh7D&!NgR+tCRSH^Lttvg~9M6M>qiDrtLoybrClE>JO)uhY z&rNxElPz@<8UqdQaPiPzJ(iOdqxdWS$NX_b;wd9{1>nG^Afs<)P5|0JW;&V@lWLB% zOcGL*3Wjf?;ft=XQ_``Ye=>f|f{qVUkgQPy2$d#mUg zAwphpVU`coGs~h= zw2ywqIhENmd>GI@K#8RG!05P$2~9UrK{M5jVQIVN>Fo^$huOCc$+G#fQgxgEWPj~& z*?BQgnm&$0tB;khbnr|CnNg~r%Y>t|>2v=0>3#lKz5Kf0)T#Uvq6&|r{l~@+0M{rt z!&{H4w@PDQ|B!o4-uDw}(zJ|+b(b3#QIU|td9Vh~Dgf*@p(5%e@#R2!EY4FW&ZkT) zOq`#o6AXUEqS0?%LovQ$gOyWW57Dp*)>R*eCv51Cq1O8b&n3zJ6)fGB-)?cIK8{n? zGZX|T{It@jug%=mMmMhk#=f3-4Z(G|x2N4Z#U5C1)o@rk#cJClJ9PBs^c#5>TxX%@ zSh39}HThg*ZyX>6u}1XkC()I)T>jv#vVWext?F_|*}OnMcfb4nz0r7aX$gmW(dy?e z93Y2~vM)6g2gg9$bB=d)p%C=mk1ThdeZd_}oV0HYC+7+&wC!)2)PzcaeHsCD*m7(v z9Zy1Y5j;B&^q*IVw5%*Vu5qZa$;%=d?_(9Q0nt+BchaePM5nOWl8L@jOV3SH;4J!_ zw3Z<*Ru)wAJw9+Yd8h6VMaSRo$8~i$08vw2H#V~jB*EteVd4gi?jq{&ag6&^{H1G5 z5j`MOVzlUQ^Fh$h?oG2nWMO}6gu)ISaP*sZ8A62m>06LWf=)(;L_wze`eFCS1y?4R z0%yd}Z`?lNYC_jKLZS3iZu3VD+JmEYzD!##4Lt%@@n>j8w>zQ084AKa0=urCZw=gF zrO0yMo>PFN_Sh1!?U$Nl^1W5ZP`sN*{o?~=b9iG4r`?)-Ve9U-;EhA)`tAJjGIj;j(PW% z^Ijq^9}|z)-WN>E;96SV+Tucas!&$DKhAEqs^HQLZkhzBqE(S3ghh6Jo2bv>dc<)IwHxSKBzV|-GYeukJ(QAox`3i-n>@KZ)tl?sf zTV-QHNYwG#zkg^K(o`|5J3a+OJMyL22~yOJO1Mut_UPwP6G)5aTdTy1Py8AO2OC&f zTG+sxJ;cUOV`YE>)g=hlwBz1_4GeC^qk`%4^c!9g89rd%8h2d^q9njLSCP6mCL&Mp z&9K3RGJ<)J&OflWK&lPu1*?ix0ftI#rCY2F@cRFfJI(^Tfh_Vp$9ixx%279Wnshv) zfB8Hmr^8?jEIZ#kr^t?1fL8!~iO&-H5|GI0*S9f06q;d!i^_tjCWSQY?+{Gpod>{aj3rV-ndj%Hl6u>?=Q!Z2SFu zltmi8iR|NfIIUjcqYkZu2YpY=e$(b1q6u=oO5&|G5j|GUr0yA4f5mP0(g#QfSi|%% z>uRJqYug^+Y&OH?68y@q&5adfV#e!q{kX9r=p>$mQVvdSoiHAXB~}onZ@~lag~)91 zB+rokjRmEyZ?)%XYiiG;A`j)vcB{4!O~<)UhPhZ)gx;96P-ED%XIxg)ge%H&AEzx8 z4#nm(D=az1`*nQHnT2JVbNUC}z8-vpPD`d2DlZ2M=EbFNF~l4FAY`Wy|F#_(*Z1`3 z_VE+gZ$gn1Mle+`wT!iTMC{fXkD|yRGyqH4g(iTeI|4%(Kp+H|zsu@@ewe$E}pq4!kqmDpYl%2oBlU%#+v_s3m{`9YuFSqVQ>z?07FLO}Vd+-{XVo0CV~yFBVn;=CV>mn0pev zc<5na@4{%lsn;LXeKwM>uRHHPH%-C?#~@bmzJzhzQ5cB9tW$9dKTj$wH;&=>eCm)$lYc6){= zCU#1I`^aDeKUYi<6#ykx1otAO86uHk4#kBfzzO)ZLr*V_cC2T-BZ*_6lrLUgW9=p6 z9jX_fD+x!5p)ze4=VxrcyXX$uw>d`ly9&MGkz>9iR~=`acr}v#zC`R`W!slsvW{8C zx8Kl5*b`1LHM-s3CErAyvN5T?IMypkscBkjKU$2k zkgsKBDz~(V%`?l{Vh~hOODmT7Iq^P`CE|^DhVCn21w!z=b+xS*#wcp~?OT5{Pe5US z$m>t^)-lSR#vI=|OlKFPdD_@sQVVmk&F@gQb*PL-XA83Dma)t@fjx1=3F}&@DC~~pbrhl z)i6NU`Q=ecIXfn8;e&wtOE^2q)17m1&VTR=4zmW&12+MNFeS`G--Iuym1b7)}aF0;>kpgYa~ z$2ZChsQX`T8v#dZF)=~Hc%wDEc?RExl{k-457Fa5IaIV)uH<|v$z3hAm!zMZY$zpD<<5DX3@kmkwP zoQ==)95+4Y=}>{T#pgVj_V_|I(^8;Xl#EMNTY89b0eXYS#^{-i3nlElqLI4=df?m` zlQ9Nr^tsRUoz@@CO7V+#*4qtD+<%#!EHsE|TjHCo;k)9K{~c@_E1xIKS?$cX{s#>; zCr&j;LDo^wi1KEU7pL>tnIl4%-=^>Uxvu771oB|_GeuwBPkUIOBM3Cg4a3x98}xHYX2FS~wj zAlNc;BkCD-j~KXHfXtZ`j@+kxm#|n?OYBStfwMmx2+~L;4xvZ%)Qs}t)z+M@w_2?tsD4h%{s`OQXc~f!<7{|s z0AXc9A1+L%OX458KJlB*#WCz<512%cT=)%!UdGImrVy_SW(0Ls%fOBo53ihP{)?*I znrt2j)vY!D{>CnC$b^=8orHfDm|SeuG`)ISs_fH)KW-t2^cRVw6~yT6O&$;T{ED$= zcf_^RiUq5~giV%u9lc;d(vmc;7Au^==mbavNg}KeC$Nn~>@~<+fr=7_34RfPY!p#% zPO!PKR|+~_-nnjru9^NXlCQ8Pm&E_1-trjzp$!*DaAhY}nweI_3lZSJ^Hw_?(Ssw} zVYp~?N{Ns}Ozdyvdmhz@(Z74V^`JPYQ$=!{W(ji$8R`avEpC3SJ?xLxE3cVfJw8Ok zJw(yF&ciuB@+OGBs2J8~+_ZUm90toWJJatFOr+9dhUh;ht9MXxCt-4a{5E@+UZY}* zuU^c`#4vFrjRy>>r+0-6pwHQP|6z747jzF1s*Z+@Nh$NJ2uD*Ii1pp)spXacraL2S|u<;-pvsL z3--$SH#xN?&<5UQ{Y+y)Apu@wwx!#nE#3>>uH%4y19-qgO69caiq~c6sg4<^GxSeK z!)&L$NlkiYZq$~m!{N^QOlf6zUQV|pI0{TtJV_4Zh zxzi{nX#JEfvF-9;n+DoOo%QnT@XV`Sr(yTQ5DerL*-R)b9GFIY&-i2D^CvWH-ildu~Ad$s6>2$q&?TDjEwJg%`b2OhvTVB69NCE z#2e`*n-dPF@bB1PaXcd+_cz{V4}w};m6JS6m}Lv6b8Rej1DLqqkMpTnxWUopv7D)l z>9lnPzlfwE*rp;^dQg&60IGIUd3=sZG7u7#%a`TBbNE~})#!i|#|mg@n4ca%C8HMs z7C0gp_C0%?lPJo!cgZz}walGH}c$Y^RgHqx(iRLDwSndo# z7t)-QHkCr-PB5(^k;nsq>`VZ330`}Tsbr)`k1oa#0EQD|+?vLWI|27-6EJtzjQGtK zgM=ig1~Ot~c2PLHl!El(wnym^g6~OL^a=!j&wQR()gd!~Z4#8jgY9P_hCKE@zQCB^c^Rvf&aATj}PYJ<# zc3XS=z1D-C+pnHW_cYR{W^AR*39~XD_N@hTovTlY+bqZC^HM@&N4i0 zH}%3L2LLv!gF*FXcDl2@vs!9xL4d#WbSf5hpscjcyEOa<^b-z$$7_I#U>uJfgmH^{ zKOPWgFj6Pg-HNxg+V4tP>j0S6$ci@e9s%ZK-|B4>UpmSYnyORh340}=(k+L}EDv?6 zAy)HKc-q?vmU|=oSJP5JlS%^r`a`F6V$eq&Ikb^-+;g z-JSKbIIMscVxRG-*{}Q0!8+{9)m|Q;XRY1I7){*d-ClIh3jr5}QOH-^3Mtk+!(j)u zTk-iYTI+I;2f@B$u#spo(vxbYVNTLz2&U&WS$Q%68#3R<)=~DKP{?b#j=Xff4_R)P zCcRRz)@CExBqXgxtNse6#~Lmu2K}kW{OmGsK1Y~sY*xT+)OSa77MeGWVkvZmYhj~1 zY8*g@a_*AMC=&_t#^3x~2VU}Ow^SY58Bi94J#kdB{HC0Ml8GGH#kRFKHe9`}Ek?sH zj9j5mV4QPirjbIVsId~t2G#O&Ivz4+RxwPwWf!+)SDS-~G=u0BjCwok8kI{FwZ;W< zI=%tp)l0l!8^)0--!*9ciK*7W%MF%%&trY@PT4dez{q!Ly9BlxR)Q#)5c7XRhBjs( zR~Ky$znp-IMy|ud!_9@*exb0F{HvG2#su7Efx-L0Kf2Z5s~vXZ8p?RH9%Z)TL0Z93 zCO0$bBEKgLA&Nj0&eC|*XXE~4pJN%ny?V0JcZ2-~_9e0qMf)Ehxgeltunxi1_k{9_ z<$+3y6I(kwsYCU)=}_nu$Mu&DL;zyL0fLeCnajoF;EY_UT7^x1h1ii6kooC@6kNd#$;Oe+5%w zIDQ^HKfocn-d1^$E|D~mP%85qmknuiS;gWhH>^%Alh78`^RSgh8&jp|$rY7;U920x zSI^dMbrB2G?1JJere@neUT$T$F^iTWU*q7xo8?oEp6R8b>!*vhn~uKP97=?!Dd3@x zL^v2F?`(>Hm*8q75%`ZD@{cpd6AsZ09o)82i$wOEF8d*FaSUk!a3p9EWrrYf<+qcr z83OaK&juUDr=Xx9Z7i%PNi7WC$Y-pJhahC_tn8@SHis!w;G89E3)n!YxE5%TzQ@vS zQ~7g#g-oQtzSvLlPLmZ);?33xBgWZPFV|xpscFf;Z=WPvhz6oT^(#S{`~Ws;h3q^s zP`{$1SjTmBiie#HG&(4KYjeqd|d7D<+j!ednK%NMoyuG)=5 zYs66hl*-A8GJ+?P74cy6wH%t~TgpY9`f5@*I*T=Aa+ohn)JCs81c}u5zBc1{WKSbV zxkw#9Sae?3K{p$XnkW`MvG>4$=p{o*($ffP=nLywt5AWYL9U(4SD^wRy=wv)3ThBB zWMZ~D06+|CU{BK08M!e(V@3Wg9c`Fuj4?7Riws^+PsbAIWbBIMqLG#~c`Pa`zi}Y> ze+I2Z4&ggCn8J-ox$IvYRBojS&ix2m=H)N?93bZk9UzDFlHipDE7JFOLv4cz*kjhlJ+dvms~0&mER3GKl9#aO9m`!)%Ez_*>fU8o(E3w?d&a$UjBasJ)#8^ToN*&w zJ04j`n})}rV8AXr$OMw^>zrB%snWIk`WorJinm6v;-;fcFEQ{h1u+68d5VP;@(>Jx z3I%Z-D-`Hp^3pPDs6yJ|LIeGjM#M&VI=~dP(Y?&9ENCsJiC!HHI@+?~DU~9>E49a3ggU4RX6j zX@$XiFeNH1&BrSLBKsCx^h+^-JIb*4*KDh2l7pRHBnvT%qO?mxle#q;SO|r|@E##u zW$8kA(Nq@x73B?;W5fGJlviav?R^j_+AZl&FI{FFzamG5+vu@QKs}OcROjN{J6vAm zk%8Ic@KtPCuO99>US_toLlL5)fb!oRsf7(<7yg|5eQz}~N?&I1vCC_t3V9A85Qwh! z>IH%^xEC7qDdfXRr#!-cQ277u4!6dU7JPzeg2Gjj#=hft84q&DT2PvB$7uVOxUHz5B56CU)z!WjCsmQ*Rz#wSzE2-yXW-0g~< z1UyMSqPmLsc`_vU87P>T+f&DEa0)L;_!{d&2ZV*!I(=evlCt+XIvScxM;))e_$-1o z`M6l*mE3flv2c_g5?{u`FCjdnsDoWMu}msEJ&*>YJ9K<1wTPRY!8+Sc0~Z4<8&_W2 zKfRYX--AJBER8AK->QSINiBtpXJ-8j^nr{f#^UdF+T4i%pd+|L%zGwZCxfKg(Jm_O z71FxDy9dN`9@tB^#D^(Z^&-p%{12Av76k&l8U`2|*0gPb^n@UQ1H}W-Tb1*Z&6#>U z+;M5|?Dof$$>Q*^*$ZwRbVO&bSq6AMz&<;(n9?Lv_A?W4@h2$KZNW&2Si8E$NQ@~1a zMbd__YYfy5CU*UF6*EF#w<~GB6Zbo81QRDy)MUbtzn4rpIhrAP=Qlq-rSCeX6;Z5k zP&dWiz~YBFSJTf{KHBuu8Ut4eq$wt5E=ER{OC3Icno}GrwROqN%31OD4fHjzL2IE_ zjM=(xc=WMM2nfjMf&VEm@E@yfzX)uf*$js?Y)khkSI3y7<~+Z2_Byo&1@b|huz-Xv zHA7YB6aE9;w(}zaNTRP?-#UTWZH6cPeKIo&*7p1q}y#X$QSoM-JYs;@Jl>G&y)VXnuO7vM#aAQ(|67KR$=5J9?%lg|hG5MBZtg zN^fasD5!s-Rr#r0ti81L*EtHB~yRGj8)oyxLcH_aJ@_GTLPjMc#1Tg1cg?~4T5P(jF)m7-@ z+==Q681=<7`qsc)!NTP)M6pUgf4RyVWxoGjz1^a)ey!FURBT@-AO*$NTY3zVN zA-PFr4jp-U22MNzIRA;U4=M2^#@`&+ZJ3sCH$GijX*6h=?7X}7oMJ#eA9w3n@KFT? zU*L1PQ1`uIdC#z6nQ|q!MX&iXlXb=ASqiNHH>wJ~S5j!Cnm>HOf?SA_vS7(Vc9_72 zK3-zkdouo5r

    TDpY|;TxO8M!@rt@k)PyG0fYZQW4{JsK7~8oive{tSLEh~v&izi zjPy^a4I7eir8ftFx4AS|qZ{6!ur2zDG92&S;i zilMW*an4p-G}`BWO^ zj1Nosca45IKrh02i|v&<*h`l}#5A`ukkVe!C@rq%iGma;!kPU4-q!>mDly$`+%M&W zmdoasd-DT^fuI*xtuN5Q?*HQ#Z;KLe5`4u!f-VyyJFIVAi#~iFI)T60oQYvCcdi?2 zKj`#tilefIo&I!huo|neBL8vj7~jU*sWp-l;W(nbyMIHTV(@(++Hg#`(MWxQ3Z+y) zf`}BY=5ceZmhMdDEdaDd3uyVEHq-(>xhEvVjp??Nts5x7?Y6s$Fsax7m)SolbAQ&8 zo;(B}fN=Pz`-Ousy!DcFdl9AiK4qymMRh;5(WF2RRs@#7{J;LN>^Gp80!v^Z73&KO z=zOl8{Lt3uzYAHIh+x{_^Vf|U2{LpB`e|@yIi>Oy1E4G)DeS@&zrN9!#q0a!%}|IXZf!n z7P)2#hY)`jv>-Bz$iE{Pf&j!bmA}sTdRcR8%N9I_{xhoRlT9G&UqnU#k_-yyKiyGy zg?QZjsHqWW^Q2e~i0+~4L@TjTZ|1YBr;b$xepamEa=lsb#O(XJs9_@er z?llY`(8?;cUZ=3GB}bshb;!YrcUy?VabWINE>_nNQ%;oVpDX3dEJ6g;S0z!#zM@nO z57)PKO)kD)J>>>1$Yv4k?7qNcz z%z-Gz=GWycyAS}rY->-y>fgGrUo+`-?qxw&o@_jr)JVNJUf91(-`detiT z|DU0%i2%G|d84)|lOpfWy(&-)*FW(dz0G^WrcZvC6ZGT!k2T3b{6aV_fO!tP1D5TMV%i#{%O|y6g^Zi?c|21K^0D>Ca zQZO@SM#*6PJM>J*C;>Oi(!4?ZyLVI+wwc?zZ`)=SrFU^tuX6 z?jx)3{$eoPko!}KP|XBnTR=u}Rrsu6j3$N5CSY*VQR(S2eo-eIl#NBh&?)p^3x1tL zSbk|u(Qt7IZ$4Xu8{*N;(2&%feVmY1RWkuoJsRLJWC1LjgYs+2AH zeH>@je<%1nAF)xu8ZEXHsOE{$9Nq}yc%{$sRj`V4KhO3HSvS@C7@cCCEpezkJ<3y7;>9c`haOh9zLlCtr9UIHxuJy?YMg@9usOfe3D$PuvTG zBQZ)1dk};w)+OTHKxH)Ev!p?QbS+FIeKS2QjhdISwF}hO2W5Y}>bvc$XKRX)2Rr2j z|Dc4q=2@`&_IkJg+!Z{L9>Ce)P=A%#A5OpTm}r5UTFC4TuS$T`c}sd@lM0TIB@Dr( z1R>V&m5QvFjZ8Un+Lyd*XS>7zzhr@fi>AX!$qz^|wWx#w-xL33M1HZNkC^Y6`A0smuF(TZ1I+;wn#{He_2bpKmOWk0 z+a333#jjfTkErnmcAuU2YxClT*PUw8ODB2zF#GrR(XyTFN{j5n{1baou)fI-j` zDbO5Ys)4(}QlwsY`0%25m?GnEFQm|7eaX^9MJb|Y-+q@SgMer&)*jar;cVkoQ5ln*c3xNY{cJcI> z9l#?dM#B%Dz}tJ*pTA_=oxJTOTznFe5O2nm=ny6%@0<=E+L@Yadi~~veDZ&}rf8AA zGSU3zoA}mK ziF@YnWti73L5$PTWf6@IYT~vKuIZy~6r!0z#a2AUnAI z{+?|bc85bff0;jZoZ$h1wwEAOARkL{dcHsNHQEahlP5YzRcw{Nu%7~IF%6T0*}scI zH;+|Y%U#sZrql1hEl9&C$CQg=$^uzc$l?Cyc68-JKu~iGg7bVuppJ{&(q(;db%MGF zBCM{5psBG0RuC79XaB*o*qprO;$c;*T$(Wt>NN$Ots=5Y_TmtdsO?y+t#^^<;-4jC zA-#?9Y%$cjgMk2`F5TK9256^z9$ojKd%@a^UiVdPb>TED{H>BHh%5kzCTEY2AuvVW z*rU?MLFpV1M7Mb&l+|a{wJ5-Ec03VW@@{{XePzh{$ae~&FZUbAbe{4ZMyjU5vUMW1 z0B=*!`1=k5vNcyz5E_Yqp{0l{UJUy75pj~3mX=o1=*W!xjSbx8tW0z?)BuE0xV4$Z zjM=#KENfq;Lf`gMZYP?5fMMJwtgEB1bk$LNhbyzuHo_aa_QLd;{HKrI9^Y`h%F@%3 z4Oto~3)aDYdf(QbNn-l_}16( zOs;KOqvj60NuVetu>VR{d#@VSKNhV9)2>g zP^bs|W~7O06Oc&CJ^^5lou*jYi~JR0oX21Q%| zhMCl{zj%I9SIM4fkq53011SV zzuRN?B<%F%e#4+PQ;SWF)ZhBQ8E4Za%rT0)fLQ}~(FXcr9;cp)Yk>wV>+e^Lxuy#gr@Ec$o+Xz&p%Tb`N zY%qARf<4`C44?~v@nc73%wKK+v+k=9wS#X7tdHzaFa1^+?_Jj5L+9Y&;e%yPnko#V zWoV$$p-zPe+q{OTF-*|AoMlD#ChF-9jjO{r6Tj)7)eIi(TLI?dJnKAJO0j+TJ16^_ zfHdCBkS-qq7)M+|jGXy62R*@uz-8;V8(k^%;?ogc-ynTnXMiE2Fy}qVmQg@QWEpkM zy#M1nuTlvCTm;qt9BYnZ)P3Y#5P*`2|7?mlDXq+-mz`lwrwY0&Wm&2d++yuKWo+8wdR~vV~na2_s76idJE4RE{JfxTfV)mvAh^o!3~JF_~jr&Unxm--cw zFN1sRG3I!!tN5LJ+pTFJs?&CfrV5re%y?zb(wPA+? zBi8?g$>V{7xgycDAoD=%hP2r7BC6^y2G{5ie$9PKg+KK@x+_>C**p1us#j89p^}UI zmZ#cFl(2^{g!PcO+Z+Gx(00#1_ce-fb6j}r5N7PC2!dDq@~IQdRC0fXj@j=g!%rN% zb@9={j%U%}-`%}BYHt0hTnt8eAvX2QFUu#=;YmO29GrlaGqn_;3D>oN)ym7#C9k0G z6-|K>#t&EM3W{NxtFjx0}{*jT)dvHO}0?`* z?#wXg;M;dE!jEcV^N~l1?=E|M=fD**`IWieA7zbL4s292upxny$)a5YrT{Q4%@zC$ z0D}JRTQ$(C86;A)a6`{S;QNYNFXcvQEI?CO8o7q{2V70;efn}3_f2M%3`>1K4##|; z9zd#i?KQ!6sb^Q|sKl=k=KmiYjF9=|ED`1Rd4pn`5pD-Q#rzA8V|f+q z8cR_$%UTWKDmhS*EfMJF%ckdSzQ7Av*(vB@hion&-5ErAci^G ziMy1Ycx7M*8cP zi3A#bKcZYi3^G zQYm3@}F_feTF)pzWRpH40Bi#q&m~+?Q&7D6waUrIaC9cz%8}|uBw^! zrPksVQwfNi8X7Q8Tt3$f!pE0~FhK)_#!Cv=dxln)roK0PpOS2$0TE-UJuQBd`ngXQ zP5Myv=6f5$ivgeL8_{*@xt9Y)9MPupeM_$ET2~$zD`|R0fB3=w+Bxjs|9D?^)dOP_ zC)=CRIq7~dFqkEEicHfHIPbI)t-#Mmlm#=B;dwm&1`_)VO~(5HvNeauadj?OK4-8p zt-QD5>MQ6c#M=fnrG*VP=rx%`I8E~n+&IsCY?>TJ-p3}DI|@#14$`$AbGRqpg1$Q5 zmsE*=LSUZRBV1jbZ%ze_A0?0`0}%@BXDr4I6(8YKJ}*0aj(iEk25G<(R4$^&*xe90a zqDI7Qd-PY15*~;arT2$bY3pAJ&2N-!KA@msKllp&41UH#V-Xoajs79BH%1(F?W#a_ zTTr4BNO$ugs>8{ArY1&MaFX#JbE-!Rj9Zt4*s@!Bk5assrHu`5-OrDsBh%7@h4a-x zp69V9)CyQ&?69|6Tj-u6ffL!3Hc$_wvWJ~vy?);JT^EJ8cxpr8-3a)I#%Aqq{Y^C9n|Oo^n@Z39e%&GbQ1*e{0F!`S`d?anEI=n53!tlbc?1e(Z!I zx_15L9Z!VIABWqsoiDYfmyK^fkPIyp5JZo955wsb3&yE( zPifO|04mKyVZu@3?+hpyT&H~Aky)JaX|ZliuzXAH2caw0`vQ3QAP85+yN&w z4c8X`!U1a;7zcsdsFzu7NXyl{4z2MLN}_~cNqS-Q)X!Lp1u5hD*Gq-qQ3ly#KDsAS+fF7t{yf7(Ou(0enI)BxlAa{u>MQ z3mQX@k4!A--O0rAi13t={0J`x-nFx{V|V#zOHXgDtyQq8e5<^CmmFVK_vv_eNA9!5OUX>_m9B%i#hG00y_f0ofq4~ zHr|KAWcjAvakVzhDe=c`Fv8*1VvRci0fej=P1-OEb7O9=4_hb7+#a@ij+z)m1>kOR zU`@J%>vX#*Vc0Xd zVPl(=W3qn~5TN|pKps;0#rOB{H@shYH{ZUdXrM`w_jlrh`}#os z;nn_qnUSIaw^#&VP_?-Co*q{iV*Rr02Oz?h(MrnQy{^%! zo9}oTdP;K6$tf&Ys-;D~`ud+MC-|2P-54c_ zSij!wQjIWs?BD}jRy~0ouF}B)5z3L#gK1q%G5kb!_+K(~VWQxHMs+$w-~Pe;|HoJ! zC^}7S92aOGP0sDOm@|QUB+{44xlJpKH1)gd>tl`+=>LCX{&G$<&tKjEoO0cN;7fDA z;0kj8KQ{mE!!0pB--{Jrp4DY`81Mlvm4`ccZ2Zm3o2Q|gu-46W{^F ztS+={2*lr3BOn4lwR`){NQD-*vOobQ0gKUKwh=SVNWGYY9~rX^p2+fPq{M}D!uS8< z8!iA;{>#$*!#xk6UGV*HooJVVudghbCz}Vqi`7<3cbfzHNOOJzN9GSAH;Itq+6sk7 zTl46;zkmK;5B#$lXwk3LbU_JQxs?DX>`?4KemLc2vldmDJ?FB(&d34(ZS`N9+5RUY zM*0p7*2IX{FLYB43>2_MpIwsAtT2m2+|ifnFiHmm#~QiFJ7(v*)J4sMBfU#gZgq{> zigdNO==vEhe8>NiUsj=m$x2C@Kg4szyG11bceq`Eu?>9AL}wXpn)L<9Bw{8&$~zN*C4sz!yT}A zx*o7ph{^W&RruZGKBL~Jy&i$n5oZ=gl=MEcpg)0IZgkWFUzVN~)Ah&KZwT-+Os339 ziCxq4gZ88Q+)ks0DUEa_!&gKXj*JT*xcwN4kopMO_<=Kzhhmeq)h)!+OxHo@|~e|kAYL{_$>bM(NxOP zP)_)M=qM#I5sFAng1CB{VA%yGvf!p2`QtH?TKWkVWKQjA;X2x<-AIyslVQ_ho_6!s zZT?LPq5*S%e>QHbveliFeg~|d_HP(vBB3&rL>SZXe@>?@IiWvKE6WgB%A_|-4b(Y| zw0Xr|l3hwHX*4woe@|%s>tbGt@Xy&n$~%BncQfnVppbNc@OM zH+R^yVB3N>aso{ER}W7XFnzvM2br%-&%^B zMUi9M$n{1WCRY3{BMR)b0hEP56#q+IM6>?2H>o~emM=HlHDA}JX7I3%vQ?N+jlLNb z=Qt@ZE8h=`8pwAzD-v$B_=v`|UC}P42kK|Z>zU~@hU7o-@;)_)z9#y7QrVz6WKi~1 z$CXPgrYU}(rq|GqZ)}`X(XmMtX8Z&_$H0U)kUx6E%tiZq+qtHECu?&8o%>qGq79)) z%c;Hfg8a&jN`70FiC|zOETPDVwaLf%C{^)e)M|&z>jDtzO}MZ||Gmto^90HMG5SN0 zK@&5+gxl>nO6vO5v5SvSp7w71-CX!;-T-RY@W;!Gzj+F#A186!#|@fv=Nb>w67b+D z%uMIo8N(c81-I)JUl>eOBP)x&RZxAZPR5rD%o;Uc?jh2-MYAPBpkuZ$q=#StTJKM9 zxTXLV-CkFTTCF2Aq(&RI89c41F1N|rdRXc zJ`2KD`-Op zJWmq6+EsXlvJMAh3W6>_an|wij(Ydhv=?t)4T@6l>#%sDvoXGE=_4kxv~3VnSWQHK z*gDzoZL!U{Hno&r-Hr^?%2^DhlH=$F94pLBL4f7{!2CBZAaUiB~i!g@%%04zO}L4(NyC2 zau%{(ppgjTtHi!twi4d4hm^j&qp*Q~Y1SnF#8OXVbgKMF=AB5FN9N%1E$WvC&ZP>h zuMo_#eGo`$q!w?qCko<Pd%k7i65R0hRd(?EUd6vtSV|h^1QVUY*rgAplL^x&7&@sm-0SC_4*B% zKMHZp1S6GRUOcT}E(e4Cmk#!iFgG_Yy}zY>?)%qSq{#I?7htgAaE`z=4RaiEEVw={ zTIE91x!N42JlCypXU?<4GX~YiEg<6lR~6bD>Ko3Of7bWKdXMI9P%T2x*hff)x%AmJJ~2aF zTPd+SMb>Ui!WAV|Of9ea)K+mhDdku9X$0+O-gxg~F&Yh3+BFu#?l6;(DD@H&@42%!p)r-%sGoxG>o%VD?-WOR}o(p*;NA1V*nSHfmEV4?t zbM}mNTbxRwFom)l2M}go{EW7z_5SGAnyJ?#(BMh^bTpsENGvH8Ak2xO0i%diO0)j` z@wXOh%TA%135jqmJp;Hdf#Vzq+%w`E=D~9TZ@PW{S$8xNOOa^O+3{;%kyQhOXYf z+@{Ysv%KwV5>yR!Ix=K;i(xe1rppk+DVg|OJ&&nS{wIP!+c**aZ!dsFdG%&GI$`0#1d4io>E1czB^gj5LLk3FEL?agt>f z-nT?nln?^#-YFq^jusLc0xTH+wCigz^Kq13HDkZ2Nh-Bgi%a!)b}N(Cw63`d7n=1O z&Kj1F`k1G>xK*E*D|K9&AA0*$T&`Dl%`E0m-M{1Cpx5VwP+^vx$COPE!h<{TZbGpF z8I;VmHEU*;!C zCPoyi_(j^YhRwt5k&sJ72aoat35+Exb8bP13{2QE=Po881@Uz{+~;{2HRLBM;PsjI z(IGMri)3AvOHW26tKza-LpyL7SMX(!eM%uj%BY);jvi&~R}lg8*fpIR+ql*u_m8$U9evqOX%W~fQ ze6zOEXe0mZd0t})8@Lfy&MHRw&mn;p0JOij6~`z_Khj+dyAkq%DUhtXYhz$@-@we{i1UM6{BXC5sPYaU4#H~W(J z&$9l}!_{FSGg>A9*Er(k$-*lM&LzfqzG)qWFF< zCxuzK)29eHSmSNn0}kIJ!uDMDv)Z{E4sAzN4V0V&Ek5bESe7unJE1FrFLzswqzo5R zS}JQG;sQ9p!89oC=rWWst_Fw*a+mAkXV%>MzL{&SEx;Ma1ToZR zF*@>>F>x3B1G$(*Vp!qBwA@fni}~xr1WYpxC2OyKyHdZx@lrd8N@cV?Ri-Auyh`q=xSE8jc$jZET!WI*&i2?y90W3gWz(+%lM83FeY)ONyU)h$emOz8`YR0U>41?syi-kzPo)~mXFvy85G)D^*8fN2V0U*l0 zs{R5Wt2s@9iB@{69}4a+fCP8F9fs`?*~j}>6Hl-4A4z-Y$fWdg)f@`S39_MvM@q*KZQbc0;LyklEGzT5 z^mHC7EBXC+PVv0yfL5afA5950wm#^yjy0(@nIESnQLQF}0gm{$sV}dXUoWCytL~jf-0rqkID^ zGx&+6!Kurgz+4?$B`>b1ir8oIi`j;u%EI8iViAgv-A6?LIhB>1tdqhGz zPen#0Hp&Dk3rK}z*N0$GTcw3gUtL|*4T_8sV9nKx3pDtBc zcWm>Qz6l#7+{KDYCVEEu`B?d~dL>G%QrAb%VdC?3S4@GCIY|_s=$}&R(O_T9#o%iA zYPs)ofhevHv=r2=JVvFK8-?^wY}h2Js_ zC|sZ=N9daG$(DBsq*&-UfSYhSZF`ILz59XiCwIb9ZKF$D29~a3o?BSh`fSbatEzR2 zORO@KKutI~R!n#>Fcs3tG=>@g)x%(*H7h?7l!zJOrpfUNSj(SgCbcU)$p@uUG<*_IBa zWv|XeZMx639{GCj8;bJZAd){iMXdFP{hM=w|4!5Yr9rMkTQ|?t8L1>iB$_k&k0GwP z-+iIU=IT($s1OjT0C1d~CLQKg9Z}){0$aO=^W%cwL{!oC5;Q?K7C;)kY2@Z`R^R7| zlS4874iNvDy78a4+2Viyf>{59+8xarN{12$Ge*6$7e$Uyg#E8f{L7orAQKFstkyS|B~8l9 zdN-7q<>J^_hIv@W7H6tWf7m&nbfI$>t!osVjrWzfRheODfNvAv^hOSLKolgFu)g9L zrZPqwf~1I7rd_vBE#I4@A-YXM2{u`#@MBIm!vNV)H!KAnOJ$Rzi8%BohDScq7w$mH zTLecWvi*9gcM=<(+xbB8-twc0W1JMi@t=Eg+S@j~^h<1{#79*>i2_HHtlXvfC5-BB z`?seHbQ(&?P=x-1O|i!~U2-xcvgVJ$2!=HTgfgupGUe9Zt^(Po>ltLCC;sQpyb>Y+ zQk%?nGjow|BAq|YQ0U~gt981DBD&zg8nb%38nePG`x6MN0eZ@LLwUk1aWyni+7@nO zdvo#wIx11yTqnXpa0|W4?R5g8t*j=`e*xwM@ukVa00+unidEL(O~z+DIX=5m@;=kN ze#`@~IN;-D&smFw`Oxy7$3$ykNy@nd`Mknyb!I7WQM*|A zqn7Cglj^XB&gc&#yMbhP;9&?28D>oHRpe)hXwEJJY9#y^NM5Fq??+wJoTG36^M6Bt z20L5#lQ`??f5an0#OwYeH znTOU~2@n$6xFen{gd6I>$ChlgUgw|wHL&t`a$hB|dkuxp`%^0`pz!1A_iJ6aLzXED zvUmhte_*G+!pgwX$BqT*tvS0ho?Z3FPYs;-_^s5F*Epb7AQDg5vQ`iFPPegnR?FSA zy6TGonaW86J>7jo7Q^fOU8AE`>5_TfzP_iBec7qaPo=Ba18(3aPx$Qi+HF}dCcVECV6JQEO)J8iK;oZeFExGlOc zOQ%$}C&(=n@>;|zvpeLs+U#F*?pJ5hC?=x%e0B2b$!^EyU#_HDSqjci%*lpJskt0P zF6|a5`m-n-E1+dzN3l6nDbxy1NfO6{nxCb{+X+>tN}W#SSu1HfJ=&pnL$@Yk!|Ggb^LNx$?t`N^9#%3wvD~Vel2`;E`pVeij9 z^asn3tEBG5wwl#cpH~K$ouo$`FB$?8AiM=T0d3p!lflDFunLcZAg^r<>+wPP4Npd4 zV^+tP_08Vd3Yi%NTI*;m6*7|7(^HsI0$V;>BbXu`YkaIT-`#ugV;yg2%#(lxJTBKC z;2>}-*x1btJu73h(uXx*aVhe696hz$Ymu*de-IHMjb|=Oe4Nl*-{=TcHSGQ$-!9^M^s*4~^gZW4MN|2nA4#omd zdgQ%Nch>k&(%~dH#E&qP72A|46i~9@mGB#Tk1COmwD}xo21o_3-@xk+bAEGuP#Ci7 zdW^bv+&)-M(NM{Q7Q#)eRbRdgjoS&J{Haku~+SlRPmkJ&1oj<{Zk`W zb=&8?L6wzM(7}62*YjuUoojjz9UEvC`Lx96?o_In2}L}LR5Xw@pHo@gY4q%jj#cp* zcQZ)k_>YOuRYv!T4h~apeQ-4!d*e#Bybi{5WNQ$iJG2!iu#sASuDqh}G7AL81^L0y z4-jRoGuvbC26|~f>-4|Xh`FULzqtoQ27#KGGr_q}po9$Hz?jgk4c+*Bh?cySq(GgV zMIE}hxu`7O2%gcYq}m~tvpv} z2tJF4;kyju^5PuNrD8Wu>sBDDt8}-0s{ve-XkP^m4Y}oPjq3JH8X564Yy?CiRZcU0 z3nmI5cwiT@zpqRPq${FzW8?J8Xl>$9Mkc71CWQmT1ndon7g*-n(PgLiRBP)_nSqJz zWa2C9g^ym-Wv>OYd}ssHuBFs!jE3q?LnP;G+BX=E(#njBhp=^wfJMVii+$+oBz^|Z z+`Iys)001#9zQ-9vmyo-knbaSeTJ1QrkUs)=odi{nt&2rZh zjgv8&*xxyz|2#^RW)OuXMDHP-qbRC6-+Vq$78AU{m(esy^sbKeS~Iz$OIT`gtDZ6Q zT2ai{~-?|Icw%Bc&9z+JsqwuzI459S7Tcrt_J#=Lr8)1&evs2NYHvt%5 z%}E^z5(MuJ%r9FGWYrUP`yZv7FW{;d7s>zsu=#^Vb7e+4CzU1ysUS7}DpkoUr}6u5 zAysrSm?}FMt&X-%#MbJ&MD-7m;&X6{%=3OleUSduxh@?yt(hs-`HY7ri#i*{cg{q( zl+>uW!;qcmG#BRhb`B0s-A=?~c+Vbe0Rc9`Bdp#oOPEZ+gLkCaakXYkV3uW_zt~I>A;rbHh{(uL;jQvMA|$b2wzk{t z9!w}rG}o1$7G+8v)73IFua0}*XRYp=(?l1qK2N0zKELvGdU^x7MH_{?y@Wc)6W>tp zJkR$lv=%#_C>p2#fD*;7d($~dO)(+6j-60`uIfzAGBw&chDYzBnye{dtmhQ=*=IO= z?Jg^2g;jn!m;V{;Nm1_*{gq&o+#|3E@ixHqc}ZJb+xfJ!5OIJpFaZ{l?e-po*qRCn zmvPkWcy{TsYJ3b~ldgd**xsfoY{2aExvNrcGYXMFMb)K21NUu&MY9;Ey-!s`OKG4a zLVA$xfUhblD#(6$=tad3A3&Sh=NG77pwI~hHHP$UF=oj>`$nW61bHpW43ccu0VS=p}& z>Yr~uZbUyE>uHjGUA)a1x%41|D{Z@jrL##2lK)LnNEnr*6f(UjDQSIh2sY5$?FXNC z%PudbxLnjuN?@zLTy#bWb0|BfLo>YfFjM)p5r?tkU<_QW{BT;(Q?GEgMd+a9GIRH#ULcY})F{jas?lk>P~|4-0n2K2!3k$iwloS>v=h`2k>V+PUPO?0wT(<54|~WroS&N!x0gxs=eveC{INy5HE#DXNYs+lsQCr)vj)Qtvo>% z=BlLAxmn+Ip*6c77GI<1A-m1*(`BbvKD5MD#OMs9WR>s(87ZS9OHFM^i$ zg}hBYSR=rY^a>jKCXVk+#@0}XKU$9K$WpB~dJgfG0lpI=$%>6$-E9|bft@ZrBNG}? zWvzLI9E@|ZRB;W}LLSsL%x?lSo4#L&Y_BYsBh8hDK$&B~dpyZ#`a2AY`)YA>ymzJ6 zIVxfnQmg%Y;l-r*F(|*iNkr6Fg(&jz0Db26v00ksAL7Hi`jeTYP-YG1!V2loQu0=1 zVVY;lPy%AhO*E3y_t$67F|&DlEG}2W2d`VBB0$6=Ey@~KI_^uQ$Ah)`E1ScKK>7tH z^Js=%uELYang#xMel|k-X*2g{AW+I(5b!)`n2euuvnenK`kkIjmb<%@4m$h;z8GcZ zrIxbZ9dJ_gLib-8g}<+OnZFqUjW(Vt_WLcNkmRG2%XFRnElm7L=EKTELqoUNP5d!} z4wPM&2ZwqhMryvGgrru(REE>;R&7?YuOu@)Z~_hi`iQ_+i^k88ZGoElQX9vV>sd2h z3lX;`Ox75FKYM?&m!VCnC#6gLK2J+4Hi5+IxVZ{kf3Yvh4D0Rb%1LX3S67{*YNY*% z{OuMNDmp@DEve3ZJS}VnhFI2SY$3t1_%|F`BtJ_4Ddms4?LV#zk3R|kS8JrK>f6tI z>AWw=`60fQtpkyX`7eOo5zDixD1n;*MyuVW)>Z#>KCTsM8_?GGGM$4(G*z3fATW=g{2>?@ zIAFeD%a(w3?503QRFz+zos>dhvCGx@{5~24L=lNV`HS0*zJN3#b)+ z<~;UhWKCk9kvF7#fbaCL&F;8@`dd&y2bl$-6l=AJoZFAua^Oep&M-$`d4dA$a`}X7 z0y)FdtYoYbV*LYhMKflCDrA(J*;KbDfzjrGEH+o8OmarczyuyLY-6?UYd;cnr@)_9Go%Ruw2XnF0wrPknS>E2|$ zmt>)0Ce!Y*3QYBV7qoDwXiV@V#RaNtnlP?FaW4&UiPQX941end6NqZCDab_-AZ-b00bO85T0hGLQgfiVyWGPD(3igjL5VO?gu z72NlY454hQ49Ddn(iQ-u!Cke!B3!8ZWR-3W$ZI_fr;R?N(+ z@M@MJ2EbyRBQ>oOFo2$iX)AHqI2@ke?huwGf#61pgskY8z=+ITD3sy83Lm$&`Ttau zfNpGXbGOq)e8JGfc6gq;SsSi6PmUQMA2v3R6KyQPmag=D-gi4+NJfEp#rJ=+%GV_% zd_?s%RD{Ql$l^$Tc7K3$F7q0!SG@(#S5*tm`KlvSgw=o01k@BHFXtVjQTdpmFiz z;oaP5b~~dTF8%MObui-a`D5ZKxUoQT25_wk(oI^^aj6_b^DT@#_SVJPXx~shWPu3a z^2`M1AF-u3(VLY0t+&IiQc^sa{?q>^>1okT$PTkUJth|6qaFeTBP?-?7fP)20+Uv=}=f`xl{bSMQC$z~;* z8XfhvUnX!i_?RH{)gxr5h>Xl-zIUvy4ukZ1 z;|Gx%rBMaDv&pb+bLY#oNgCNMHI`(~vkTm2*cwqr3kPU(!NHX)Ejg_nslR?j+No%d zP<4S*S`{=1zWincC`u+a(-2tf`0w>ES~nJRi)e<1^kHF#Q?`v{IZYkhOF=n(*w zpqz{2_K3s%61{b-onfHG`|h^SzSs!^wk_7wDKCtioSZlY+U#}PXZgna6%qwDi>07f zT`Bqx8wc_kRO)>ZXqO68BpK76VSvjZXOU3rmy_=)#xv+B>`Z(T7&!u};p6FnD=fc# zi<899M#|pV4cRryyh;ppOd4;l#*+D@OG5ent2t(eP!RWeJ0rGO1%B4b(v{}oR9RS6 z@4cd89A!Hrc<D?p#+(Yk=g;hXaeEZqU9-?C3 zAu{3e@gbyba-@cG5SZ{DYjOkYLOrBwQ>3CvVxtpf2;hMR1i{7ra1PV!8{Mo{Q^B%F zU?nQp4Oj-fo1nHEC!z!9gIdsP?J;9-N)6wFggj$t5b1c$!_CG(YSMv3traLlCiIV` z&THlw9&;lgq0Qvf(ch?ie{vEkQzG--+Nab4yLy9t8>q|bjAvi^>UsmxdJ3!mG%3Yx zXndvQ1}ADd>vOe)?(cqrd8v(quF`&Tv2TDiqps5p!cfuHAFnkukW$qLKH z$SRM|&ak`RdXbQWaY#t_h*2Z8S`FeCE^SU)4`N#&3hfOSm9{y^+lEDh2Eme|gDk~z z6KpI`FnV%#6&hLc7fFa^sLwd4Z5MP>Ha~8hCs+JyoS&O4j!sN5we4&uHvASI)me^f zU8V5Jmah!K1D)6=)(tV)w%}YWEmS_|%ZwT>{I0tfdJ}P*q`S{JJ+048F+)Pc*p@h4 zi>%pPjr^^AHdNU62+P$AtZ3UEw;LX}6SyZE8Z&hnA;dWELusq<4BAAtx zSnD1T0*sbo{H1L4EaKwKIR-&lfNq4{Dw?eQmkCYB>sn5*ZXm;r2~g3ZIm<-Kxk1Ej z)4xVmNR*x{RV+!KxIyBC(8$UtbTsG(nq^-#IqjteXsh10e!gp8jea&CPbp+pbdEB5 zIZe4U4H-+6k7!um7E+}`+k;|fl?z&maEzZsonA^Xff}?UZXGWtP*0~#T5K(e#G}fM z{Xcq^)4|Wj7rQu&;QM`iugTio8=23fizHVl{{v;K%?&q#MWcP=p-nZVH5f9h)pqse z`6&g^1lnTsn)$sjG>K{5<8!G)Jhgq?Q%&aA|Twu!5v!tQJ z#KO#R8pEXF8oMPlG`x@=8Ne|WjnGU*GzN?}%9RJrB_ux-nE#g2V7iG@S27Jh((`ub z-6QZNUk%nH(?AdJX~D8&9Cgb3H0lklwWoa%u-89f-2D*N;cqMwk`lWifeXah83X_; z;2uwSv(q^zr>7>RFaS6KU1W2=)eH&kmDr$NogUhRUw7Yz+K znmOlOJ|8u+jWf<2PY6wY4KEL3Po5)AncEz=M4U>5)eXq7TFuJR-q?j!)XYJwkbUxy zlS@x?hzOVxs*n}kXM&`Vs-IRQ4`9*)QDPdFxx+hkX~2}A+}D1$&-2WLt|sQPrm9c- zHyT}#o=flbjhj5N;0yamcqeBkJ9*CjF1ryCFosA&X*8UT$#9pCd}|8c@0TX_Ux|JX z54tDUZqKr(t6jx~C_|&a#)vZCj~X}ubEdTW$fBAS8aH7}*J3S+dIzCE0|&W|#%$Kv zf#C6G;_Cd&$pWzDWTW_C(a`kSn5P$89-It>m+`vpSGwQP#hKT6*p}Gthar(Vqg?5m zU=LFUm)8tWpTM`_I+?+106cyj?1pX)pQc{pB!{1BDni6A+@U_;?v2yOn)Wbua#%ZIPKkx%Ass*SKQ!Y$RWHE z+SBOO8tKeB&ZoJTWP!U)+|bx`oqd-5MD%i4nU&Y^{Pq;s;U|^-GI($R!<5w7)?8c9?)qB96jujK_S=u6r zW?q+o&i({IQGUQ!*jK{3((vuTR(2-U1Ear-+04&kh>;q#Sw*I3R~@(Z()Qsrx}(O+ zX>z4qbn&ElFQWz8jw;x6WmPBh+TM-YY%=Plaf5|8bNW#&TF@PUVZr*3b}I{NU}!Ea zRyQMt7_AYS=WQJy*vh5<{w_jXQiJ#V{#Lg_0gYwD2Y{N>}~YO-|A7`tOJH9A99#_iiD{>(K zUOjr_jq|kL%+g*#CBrP8m4t*-@UBL+hCD_MK=?pl+_|G#4%j*OY)&%PRXJ6=kTA9k zUEu1y!;PS~-CM~h)5g9IwaX_I_Z9N`*FAG|`JInhSHWm&%X{KG01MKQ$&AtSRBadxCtnflQ z57L>k7qVnnswCCDPuaL~CXbY-+}#;zNKBvN@;p{hqYb~{@o-uOKWY84@Cg@ml1qti zq8y)07jNSIoXDuN=DMAFzxl=wf>@_ys5MrMLJ~+1-mRv9J#M-!GR|a^+vxA+q^s!@ zZiAk0YhJ@cO@!`4Pfi*(alcJVYdLux z`TZ_8xslcMbY)vT1B$pOu$rsOjx4Y3hDCdD)d5nKgDk#m5mIlKxW=TT#$)&lHXOb< z97H^q`gy0IfV||qnN>=1A)XekTfmsLKIBI$sMGf|dRi&Ji1i@Q&^`|-%SYVs1*i>9 zjxGa{C~w(WhL^hKRh|bIF$`W>vj=&jYioB6f*15KPww?nEFthXL72kN#jKxB&QBt* zQ$%1)Dr8EI{W({uEo?&tFI%LM%$ZmjQz(ZxMVJc6KYzl4>(t1G5)Jrz6#V?ogNJ$%3Yn(_zrcF&7ig20Zz- zLIyS)J&P}v_^X&x;7=?d^uJeV%UNr+_Q|mG4b}E}wwIjw$rb4@zNtt%k4f|X(snr* zP<>HUl%kL<@~%7WHyZCCA$(34jb5Emf}LdZlKCqh14@l$B>1kB3Sfr7SDu%LhWec% z2n7^3cKOV0y9omkf4dm5H6$xe2+$2-t#0ehAGs8~mQ~+9(uw&G-8N#!3{mvH4{vJ% z8meYqWC0_y@SyJdz6t{_zU+FAzRhpK!K=+64j9g#nWAR@ahI$0g(F^}g8w>L|NgmE zY8G}P%IvG2Tr8`)!2$UNQ;5*1rIUoafA|acyz1h(;M8%dLsu1zX3w&$92+L7^=P}g z9sS&*l9IiQC5sgW;w78^{W+#U%&&k|mvwHLV-gv{%sUiIw_x zFQDmSO>?{3L`yhX{S6KZ$G4b>*>Aw8qzTc0ZYLB7EXD#zu1UA!?ht*!Kg$M2iM9Sp zsq-eMF_Yjpaog*QDY!VlsTSGid4;{_Le(~|=m1i59Q$`Z!%o~Pv7~#^qP&+mnh>_@ za|jA5wv=y6C_M0Yhfh)d(sfT;g&MEt535IL|FZ+izMA*?tDKTS;6g)1kBpx==7gNO zpNvORl8RB~W4>>b@05))y0}BUO!n4GbvpJ9U5Wu|kB|@2ynil|9_Fe{wZw`-(kQdB zI3%OhRfb*c8RU zPFiL=fpt4RME`Q4HVp1&&W(j#Oz?M3s@ind{gUhFZBhQ_EiH@ag;jEBU8g@;`1xrj zdVlexI3|#V{h$oNC6$CVEC2;nyK=5jG5vE95ah&msKicNU3i?^!@Jq^2YaW!`oex($xQX#ZSYXJKfiy9uww6X>yz5|5%My0bjiy>xnw700O+VBs}?yi7)Z zapyZ*{#_9*DQQ*3r@JDPTF!roN79X=uDLGvTUL)WpQ`a%-pB$Q4}aKhy{6#!6B ziEFaZW@A70-l@H`+iSbTPOhV}slqm4r>E^UB76m+J1)&HpXQ^2?UE)H3)J^(S-Go4>}mGV;TmDB5Khu zigF@c%$f{`p>tJBMK6YeS$$SKx5FyRE7{1Xt=;_!bMWmpmBU^W!|TGHCJ`GB7@z3& zqc5k{AGrpBf`(&pqW7x)`p+XSMKE|9XbTi_@c8A{io&Cd>zvc0gG*>#KzjO=mMUOs z#@R1BqV7-rSMM{}K6jYd%WF`FIp|KAUcPz_0LrGCnK^xW4k*0kd^8mdQ7#fWfmdkX zd{RndJ)4mVleJ+=upF>BD+_U--53%;y2UT7pq_$Y0Z-vZQmDOwo-flYnVrm*x*u0m z5-G--Tj=iFuH#_qG);^c^%xM4AafEB*<>d)^Sqn3hu(Ld%pXOjhneX)PcrmZPd-=P z^NA;y)$@>$C$d240FVehCaii<=uy`0x+Izj=krEU~<=ULD zT90yGw>3qbPd7X@HN`j+PQ}vvM&$>yF1_zeNoVIT)>=!F+ycw;8$E;ZW$CbNaZW5T z|9mE6pIQLxQXOF|HPL=yk5bn2;x4(gQjD&*RWgmI^U8?{xU~nWlG~D9H3k+Ic*P`$ zS#5qY33i|H2(EN(Yd2&$5}ubjcSr6q$Kb5PuWLHQghbj>)6WyWK7t=b=oxDv3bsXV zqBY7qx~6vC(8VB#1@>;?jr>U)pzb%)&}~O%q;Qy(joV)wT_5a7n3}AiJHU}tS>EjXs2^Z%sx0Tx(wJ{N?=fZpXs)8W+;*gJ=#1>WS;*hf zu-%-opyb5b>L6>~8iW@+*VGw5RPE}a?_%Jd6+6exR6Oq#hl~mCeM zqWUh|1>h%JOe}%afgQJZasp}@HYQ{&`5Vfl;(Ay8xWoBOaO3k_+$2Y=T7efvGZqye zqnOG`v^(wRQ`OoKA(7eFXF^>1qw98CviJGJMgr1i&rMnBDrE#jOqf(|H9wzY0+Q$AlT|QX0j^mZ`j_0Z1;+r? z30a8{jG7n#K@#|flFeu-9x@^xP^0pn#Q^xG-%}HFG9X+0qE4ln_X>2R84cOsmUK9->c37p zgz4v*dkK>Eve$;3xm}DyPi(Vz%Gog9=(G|#yIh~Dc(t!|PtCS|lF)m7muqZqGucBv zPIDAWNNOhL_a!eB<+3cZV2!KcGjMc4C(nG6FD}BSwqdYmCm?vdhemN%&w*Zr(jylk zvDt`knN3J0`FLJOYTCWy(qI|iNj!g4pPC=V7J~(@@`jQK6LB3bHSIM zY{Xv&b6z|;BIUk4;9T{ID2s?s0_xp&@)jEoabAyv>-r({)c6GRo5YqgtD4WdS#z~W za&*%BVL1HjeLOPr){ylMP1?&V`BmR;Vo#{CuFd9a7)Y5l!l7%}o4Owh6vhmqlDmE{)3m`TMpO6uF}&-@5Sde?9W2; zsHiZj`q$3IN~NNrLY^qgz_}*}pkdr!7&9d;q)9#khS*Lvr21JHr#U;ae17VvjD$n& z&bTT8)Ffi8ygbY+6;!kn$I99Z=fth7X3mCOX(2VYf*BzT8xY9{8gx|ZRW&Z9p?Ys6 z(d`^=S}2ZsuvY@RBYmEJ@ccBJ**)2)gNT8OTbFxL){;BA?_;xS3j7R0!C)`xA)2QX zf)YPfOY2I`FVtQ98ME=2D*2vfSv|YKl-vdgPe(t--w zdk_Vh+6p2n0LbEc*!=`NKBv@t5eHbJot9Oa`Q9}#A;#8gnSE+bH0s?i@!}-L=wMQ% zH##g=_4l!Rpy&br8b&{mxE>8eHS*%X-Sh&T*^VTysO|A8*gTpCZu-kI&KK&*pjq0g zYi{u7zE`h3W=8OsSNiuuIoSY?ic0A9r&OZv#FeFVu!7RB-RfrR(dH?dZej3uenBxq z!~OtwIr0^F(20--)>CX1H{B2C7A^>G{6f)ePmHR!U& zTJ$3NO%889E)oGEw5FxgQwtPnrx3@BM)oQWr0;=vwR_}X3Q7D5YqD?nLy7e4f)uvt za$gNMnEeu~+bf#6WAAox^+}}We(C#pgGCRkZZ(!7Gg?z)&@VDs(84xg583{BaYgi& z*_d*Gk$4A}O5$u$o~frQyH(T;myX%q{dM^1{Nb$oe)^$M18&#(y8TJSy-^4EQ*J(+ zKE_wl%KJqOJRc*6toeceIa76CP}}!?&{ltbth%4{7wq4{ye2_HNQ1B0_;L06WQ_AY zmg;6TMs1a-DT@T&k5=1Q0_J?4M{-Z0hPrQ~^YQUr5%#KE)yV!n3iNWWF@NTn@7O$P z9U^k$6u--~4b^!TVZf+hr}Gi2`kN5>SCjkaEDhnk{a)$j((JZ*^l~PtF($coCIRr| z_&IP3kD(Sb*1xHbu=)_|t>9Ocf*7f-#g~?{^>Q+JH6b1s4MNLgM0KvotT{RXl;ltSvB(i zX?KOS`-%64p))ge%6ga7wq&MQ?RxkB<#;Q&Smgs}LcuqmSe0&475quLcx^vx?UD zTzs8?_k+{TO(ETpee^%G7(Yvp?) zT_QwA<~42S=Ij>Faqd%J+n8uLdD@;BrN~H$?T_keQ&4!{o2jqXxd?h&9Kk`azoy#v z9}NdE<`Y2a(k{Cm5gMSa;XSe>BvR-SOMe_2MX&FmsG&?sQ3J2CTHIqG$Tk8#XV#MG%9Q~G$raz-3FzC=uJ zWeJ&zt!$4#e8moaCFWyeVNjcUIi#f`R}iZ z*RbRj|GO3d0Az)Lz>kEeul$3<1IGi_Bs&V86Z*ONIf#18Kt)bIeqK=lo$pM?$u_lq{4i3gV&ZEKYYh>k{$+Uw5m_xI)!Z z1~dGP86<84HU$46k^1^Tb8Y*J_tskvrU6>=r`4V7mi>}v28Z_v^EMCQgRe+c7dsk# zxj*b_-e*NBYsCGn`STWQS3RTqoze(WGEwZ&RJIN5P^aLzvSE>kTT1>njstDh^k0); zWAr>ALQ1EXEN7(;1UN|2?b>}r`C)I==R05qZr_viZ^_B}!7k|`PaWWQI&oS6JN4Fu zExNu|B*!={>Esu%Yw@DN+(#%Pls}qkKCgkD7!u69YXBTPw@Mt^wRdnt2@xhMfmW(#sg0O-cBLFM4V@Id zw$#QAB_qjrvDV^z`s9c?eX(`pBw;FS4h?3rf;{+-e$@O0#DD6E+aW;o3SnQ5ay?L9Tglq&HEX4k=AWMek=~97;fwG&xzswUg^KgN2&8vi zicMl@oldoCDAJ_dR)kc)7V?wGK!Jb4Q>1~(<8IF`PrpI;au!r$sVZ#EmVNew0 z#}J6*kC0aV-$O+O1%`&3B7GtsH4(M|43Z!|la50T5%a8dL>^~K~qf0e&w z5c5@xr6#^t_ID5PsQ&2wvD-Oe?0l%CreYBR&-tsU4|xw_@0kTLJ1xVrRU6#W`rb9^ z>*xUw+ug!SZBu7UJ|*RGcT1SQvC((Nupxc0AF6-oD75QIx`Dp5qRZ~GDr3}@H*ql# z@^9roA0h%YgGwai)ge3wl^bef@0|E$`3r9D-B}7aTAr_^b-bb#hwco6yu*XHhfs*^ zNN`Rjjccy2=}oJ`hl5x>-rh>eNR)Q^<{EbYOnEyzBhATZL-y_9f$7eMAaa?nABGC@ zeSSFMyQ38Ift9<1!&y&6HJ2BcNy>iK4$gqf|HIoeh2JGV=XDcH-&#HfHcLn%JC5Tbvqf{|khzjoZ#hPBx3*Q0i_mmcO>mh)j`UobPdN%j|A? zgHPmT-PE?-s6~;`6J=s0uD8O9qr}`dE%yC5S;oty@Bv-1Iay zd|D=MneFMNFIl_Z8{xrqV_5#AJy2aofw2w-RN~Kfp?MJDp{uTzNPA%?>c+i+wvGIR zs__d@O$WVKFh>ybLbvHeJexv+Q^H8OYlI2s3AKod?l!LEz=5{DkI>L6m;EFZO?3OY zySw_w%0QUy?;dMRoUh08_X`|zg&ogl&3H6(zFD#F&0n32jBH8_Am({ta8iG85VVC# zsi%G8-}{RlbG)n0wq-Z}K&XA+Q|L)scmZ(EI#{arJWL#Lr3wA(z~gUA;NP34KhzGm zJk-WUz*~YHkU#9vtF;WqbA7&%YJ1$OTJ=l!8tR=nhcf1*&08YR@q}^lp(ToZYCdn) zVs~#B3l*w}p8%GV$6Ht^i~0|4c76RfVE$ktDzGz3+;XTc6xPC8TCH1^8{(>tU{LP5 zW~^6S33|=qaGYFl)@O1$Mz6$H0;1rXPD3*>&0vf!V7c`+Wp=dmYcbqVA+ex?LLno8 zvKrH>#=3WQm1v-AVnqae4N|rq53rnJlNf;L0PA(T0Kf~gkoZ=#`~xz z6m}!#muB5rwlce|W-p6y+WCf70WYd)RU_N|gw1y*=~Yo*MbnTat)XA@!{xzR@1<;I zsooWHyJOu!qwBf1k^9?Yq10MzZaG;$JCsD&M+zy4sZz-?UMRvbi+fP3cV=^H6I!L6 z2^)q_XLWC{4tJ(n@|;0PjCnM$MQcw%i5?_ZCwx$8+3E-CEWjXzw?rSWmM^QS+rW~lSW2ZqE6$|a3mzL zZ#lj6uA|d?Bfj)~2H<~r1B$zBz3txfW=j;7tj5XPT{i{<2ANwR-NM$Gikq(fwq&Hg z+_>9jKljJsKtn_CG3>e;#6Jw0@qvqg;CbCMlC1s~EB7%vC(2#uIE1f#UYor4-Q9G* z4oBRVX(=eNHv$p{6RV766MvF2PbIU#26aT7ARqg@EHMTqGIs5XcjAqufV--yn2)@W zg&XWuF3AdE@a(L|!NBA|TK>DXXGS?rCHdHU0_EKhp(G-Oot}k<^uHQtl8sjCPy)YNoZTT4Y3a1sJ}rsU zJ6QZ`yx&+I5>%W^0S=8I${eH9F1ZZ(6)rps_Zq8|)6)Dr7XO|X;doTGSm=BB6l?u^ zo)sC?(-r-&&@C>=Uf|}keM1i_00onr?DUd-rCZ14cQH}@awRl5H}yClJ^L3LPVV=u z!l`ztIcZ{M`Y~_8{6z3TG1(7o+aUD5`?C+jH%Q}p0zOqdLfl0*-R^5nx3Rv1^~b9% zq)QRFQsqMYS`GnAM(8UKguB7AC@3 zhH4_Ey$;EW3Bn*k33EoH@yXkW7(rkBy7KTNN;(RWF1jQun#wZ(TN&$TfnTO^0ikAL0T8fW=ef__0LU5EKxzJ;zZ&U4^E0f#FzwUXOPB*znHKhZ77Ub=aZ zZ^(RPv!e4;p8XCuU*GL~{I%e9Cg@z`a(O(P%?wYM(=|^Rv35V^=$`nXDywZ9^4*#> z)=DYG$Kj~zbnyMW@Qn5z!v2q+PEO{}=H`sy2KB8`?dyd{T121yur|*jV!gbhE%4O0 zbhMOVclhTtD;qBzK01%w7g2&49HbV{%O8U;cM_C@%xz6w@~>M#e_#cZ7jr)jmAKOh zvcGG&yMN3$?IAzsDy#Y)7>a1SJE-~DTm90O>i&U*k@5L_=G&2;aB7}rbr$e8;jqE` z6dLnJnYRTdVu_vzN*9gH|5c7G1$6#I|2cLmhRTEWRhMMb_Ibmv{e@w~!|r`_MBf^H zY^B0tcy`-F7ZuUaO>~qxN(Ev&<;nj={fL6JXzHu!oif$(63g}yH}CQ>;M*yS&N!UD z{SnJ7W&URCIZ3eRQPe3kA^6V@g$Fnk56G;zGpC0)mipZR>waUnT~`VU4GxNFPYu3_@O*j|aSSn%$lonJOKKA)GHlJuo0DgvWpV=P$FleCpf zKCld6J}xXuN?-@pTaAXUfF9Q=7_f#%HQQn>Z<&Ag; z2}@x~_x&0A_XTiSY&E^s^Gw{q(c$?94W&J}{#jsiW(6pRxfq2R<6UIsMqe61g4KDT zsJZEC1o+hsW;y}XzC;u>B|9^1SXN2egvTA|B}S&2#9LFegPcKOv+5uO)cbDp;n1tc zc-?`0r7}8R)N0F-02vRThUn?llZMZ0zn`k}{kJn_T)1&;i76#9jU(?2VL%{+W3;(Z zGI~3HBr35Qvx%*kc(8al0(Yi!h`Unb=PT+znP!fY!wg@z)0L8Oi#KhZ$%ZdQk_3zD z8Wu*IZf-xT>goBsZa^3D0g|v?i>vhIesMACPVa62V_!^6M@OYV#ri`G@rR?6msZkp zU*~5yoiM+M2}me37tZvx!GkbRR^}%nrskjoQLUawNx7sTzwYWbC~e_aUZCc?GegI^ zx8jm@_$@YG-3<}*-i8Ptly-9??9^Nn@eCe>W2*X7yvdkiA_5}Ys)u48o9_6n%xOs^ z=3ZtS_eumX{E?%x3#reE23-W3a zcUce756Q_-ekwc%17X@52!1BT+yuCp7BbZBBALvoShfd5GB9QrAuz{`mZS*m_Fbb6O#Xn50@JMFJT z2f|SaYj5Y|1cqutt7`wmer~r6P3|TV6Vt+%hlY@m1HrPk)8iJiH%7KvCu5xK3G?31 zD>Sk<_LOT-cs&@BvCia$hL|pWKOr@`9(--tM&wBPCQpnMk5(ot6 zNQiGUPv|!Pw#s0GBxxcFk&iI%Pmo4u#fUi-moOfOFa%F$2%fa27PgD4V1qo`j|r{z zTXj~B^);{$;;mnsYjAMr&n^3FHTeFs*zgX146fA`0 zkS@xOM-C$3sUfT4dX*3pt#6()%>!Cjft;|`XqhRHP9+9r1cI9#$ z1t*^QfBZ~y78V5CWmOw39W8x-??^J@GIX(tS!E8UMGf0nv(>GZlU37g@Gf*fA{*)# zObpPZs*=T;}R3UD7Z#3?B&iP>zYJZc@tdQXX9 zHnq{1P6POZC!!qN8dLWi+^@NhdD8qkA&6XocNE^$MM%Xe1OZJ>gNx}7?n3tT@tImh z>nHhISX?Mi()zMmkS=P^K@*Uf*0R-|Zxi4neW9qH(_V5A&VB8W>3&!gsJH?g|_j_m3AgRk&kArzh6l1_Gxn?abE`7*Tm0A-FLI@KlyNy)~jg+J^RhHzWGW-V; zy{-or7*0bt5H$>%F|`ZwIt9<@UA~@H2yh^8S;}f@-d6jAlkR*KIy&4|Z-KRyB5xxf zAtRvzNhE-3e11GsB$)A|;e~k{T(+ONl3a$5ok_fRW9cEeVm@> zs*hejmTQM$NENHV^s*kCN){HQEspV{j=zqW^(uzsc8DFm1Gutc^<%mWXzgliE#p9^ zrE5auiIX(Ts>2|Y4X0#Qu23#pN)DH{b-G3$#JVn`;b?+fGtK1lH;;P1p9+&%jp(@GI0C z6TT>yioky_u9+fwtBrzU;5`PPxmtJIYVRB6zU_F2N=jSj3!L76#)o$yGpzbd`PgG0 z)CCqoic^;t7Ep)@A5xl{U14Ck!{kXf^+cxWZ8l;&Ywaun&9REvi+|xml%Aa7cPUpj z@a!;_?KC?2uzggiA&^W@R~9TUGw>g#!OV~b^$63buUTK}oRprHVL(KzW+=y1Tkxtv z$DZ*F_h8^^>|R*waQPQ+P>qYT_u?QkGlQID<|J$8)zJ{@GfkwM>~PNf?x$s=CQeXl z?(YDU6i5?&sAtF~boLh7oHQ??M_J=(Wt<*bm@C9`Q>f!p)5m6yg!^I>X zdoyl=@3a!J=__+}1BaJzYNiBxG^tw0#W9#sqGlQDf#_mv`Ys<(z+e9idTqJE{j@Ii zV3G@0j8SCNQ0j_G)*f#C3Rg%r=?b{+9Y;)JYPp`&KIag@hFrFG=j~`?_uH-z-DWFp z$NlcSogD=@)c1Y<*tk1=GHwUve0nk#@SK6cvd(M8h|ZZ>@vxBA5|*vs#Qf)^^F3AZ z?cHrNM0jtcGVRtX=%JIZaXZ>6R41>#QVdSd2KWyf%HvB;D{tOQsV4Bp1ky-;b+UWED!*crGi1j8RS3W*-E?TaOmzp+q zc|GsCGnE14$}Y6WmfFbBv0aSwV9}Mmy$1>vD9=NI9*>>4Rz|`8Z{$g%ea8K4DX`^Z zHGZOPuT%1YA-<{dxrakJkNI^lsLgee4GvN8?I|^AqM`e3sq);g`Y0vo7%WCrX0mTC zx);&@#4%noXBFyR;p-f5afh%1LqBEv+5xvDMZ1s+6T6>sh1N7wcr=wavee?R#EVbK6#yT8oe2rSIXN+il3j$=!lf5?TXFF;i>yzK zk(qUBAs%aMV9h3#+bauYtVNgWIHYD>TSHcXZ8ZJMGRbQ{ReCbM87QZH2#_#<#bkKd zE0$YWJhT5mproI%yMXODzY+N4mZ7r_(4;^hIcS6- zVJ`Mzy|o=@W#FX!rc3lUXV-uRPL=q7LRWB+eb*tg)u_1#OV(lp^l`?2exp2Fyz_V& zyn89X{-(Kmidb2V;N7GCuFP71Dff3=2=I>~?{@}brG;%H-2h+}q~h6GAX{raodU~w zn?0Hy6a&Zg?1E=d%fZPhQNGycL!q92+tuas9OMS8E^a3*Y$z^nCn;H%FJBArmdXTf z9Ha(9wgbdeM!y*o>%lk}g}G$ZX}4wiHSZp(Q&jLk`2WQ^O;9sQCfvI0A6*5t5ofb1 zH)0hDJ~FeJy4u&VdrQi&t8A2DHGpV^@qPe_=tw~m(}V|Rqr|t^i|vX^$tK+IDPH9k z=FpFg(n*fc890!87IT)%(kwQ=gvcB!?B`((D!V{K&aKQZRY`6^&=FqpIXZ$7Pjs}R z(XYohz62WyRbL)s$|cQkAIye2CqYy=+1-?FcV%bl$ZWxg{7pL{|G*&L`w^~=`~hgm zo7L^ax-&D?Z~IA`h4qHw%cVM+Bnbdb2uM?-Iub0E3e;8qG~%0Q{EBjrO5Mi!zsM)3 zf~|LRJO-5`sr0O^*3X;ZyX351;QYHy?Tf%*bp_*jZx5d97{-z2A`2UUlw#*V?FdRH zi=!*_-AFVoRGZS5_QXO`>7s8ZyOXTS5?I6BY_q{x@$o`q5`%dsx~5fNy>gtL zh=lopiWt%0Qr4ojtJSc-?Lq`xH^1m@%`-$Hp&4gN)z`yzY6i3O3~#1X;S9n(O;V&dPni8+*y6Oh2_t3QbnW1) zp}jt%T9#6bBUM^AUQqkt#r-?e$6dB5H^0n)ShNeWWc?Vsjhu2kC^P5^^> zP&<6ebtTHy*6L4=n_S<&z$uMJ$MYrBaoP7NA9Q`oGluAYHr{{Kiq<+_FlgvizVDe@UAbqbW~%>_Qv<&gxHveQI&A~2Z#zhNj;(qDw6oN*di=QV57vGYVD zBm-{{u!*>3sIP-vH4HL3a)ZP z*AHo8zz_jDKV45x?zw-%Qhp#^O&}Ya-#tzBsUFtwV~l_4ihiL?H`rB5zCu zKvMn(AvK$#3mEuwGWw@_)jDg zPH_QF>KcJM~KI zDgb+@>dLT9c@fL0G*O`c2nAxln-yC0JJZG<%h+$tEVEe%pOa~*6*fQl@hYY@iii-! z_v$tN3B7~u zPonG&WZ0V993sEX8)i3HL*He3fKT-BB+dh|zb)HeI8V-!?IY>#BH=#=roZ2rKYgHI zpfzE4R8cML-)822zsrB`7CcmdPS!kBf)%m^Chl4CaMorzv%L{e}S6mq`6a{+wsQt*e;M@MCd#&z;$ zX`ccMbqwOoX3Z9&<=)3X*vw8e+k_+3I%;YFQl~HDh+G^s)wI0K?seBAJ_#8G`F8G@ zrcQ8yT9`s=Qw?L28aGhWhxM^7|HAVU#Q|K!-H|+Dt^Zb@zb&eN=oVox zN*F~VAP$c9)jKPFt=K)YT0R?DsORR#J36drVmKDQ5^ndaSW)dVQok`0`&a`7gS_{v zgbs8UX?nVYyQSv({e45|51`cZgM;LZ1O?A8zN6dVx(#++`kRG??B~4$ePqzzwV64* zDg^KS)pAeia-6r8ZW*pSD>)V7kwO;vf03^XaO4R7+5BDwgXm@c?&lU2kIU4J#PIdn%jUw|BHCHL;f^XF}LcyXO)8 z)t*I9iyZ#}M7G>Iwh6`|j$^T|GP?bj|Mr`&Z1!-g*t`K>RoisWrE9ce=F9cySF0Ws zhQ^v`qZ3&%6Qw3Ky0WpR;|CT0bHZI;gMb*s&S+OZdf%*vs@5vVL|O{FUeS=END3o& zgqv$0m^Uq*Z1twiAY7f4#)OZ4DZ$phZ1)9;mWRhiS}IYbS%$LgU)vD`8zfxEOs%Rf zc;0$26BAtTBWm5zvxnTT#ciR&Zem9lUFB58uvPG zq_6K2LOC|&s}C|M%H0fBDT|YsfpZ`dKbIu!K1skUHX3Y*XrOBn1%^&yj+l^P(Pf`` zjNO>*S~7r2b9re4y{GY8Jmu7Ux07zYNsDVwP#+K5Iyv>q60HD<396pmYMb*~AGn#= z?82pUPa!cTSeiL#bZU}yP0{{P!%}$q{BZN_$%bme!m?wD35*NpK(l;$y_sBl&gWf= z6|p0pIj&-`V!i@ax=)Z_V_8k)b;OIwdovQu+o?RZ2H$b6HyQ2T?%A z#XMK1u9~wHq-s?k%^pq7__tfSxBr)0N@geZ-5CqJ_rut?x zf3Dh1AU|@t^gXu_z=Kvzs&CQ<8}-v>pL99l+ThQ9Em%h6SgpflpP(Zv9$>%TAJHv0 z&Jzn{|Ce=$0@4Hy3JL{6vOi)(I4dvz@bCZ~IWBiNL2RTU_<1>|I(bJDK@x_geDvgRfH8)yHj2pb^sY1F849Ng8S~)F16TgDAiPU`FBm)^lkp8 zWZ7kDl=DsPw8ULOq#?k;_A7@#N(cp`thAE$ZN0DEHPo&6)w8AG#O!3_+|*2bxM6ZI zmNE}j3yslz6EctKV-`okn^H*mW$Zu7%x=hVw-6^KO)sy()2LjEEne%N zo0@%{BZQ3+_rz%6X0#@Mc!8{A<%SL^@^OJEOcHM&ZMM@8NP?eEBRrFHbxJ|IC5(jf z^>su_hTN0Y(hX(X{C>;Hf0hP}c(QR#hH7SV9$gge<*MFDWjbES3pB+MpcOe?RGu!; z>7Tx;;oF+U<{a#g^Db zj@$f3ytnBQQ~WPO;lxBL68vfgb*15`uORB!=w)k4;!LV`8^;=qO!a zlY*$N?)Nl1dv;%ID~l;{Ib-ZVmi?OI?LLra8a=!Snjb02$($S|nVGrWp3M$EK7#Bl zFr2-;y?^^&0wA%Zu&BuzR7T4&n|vgKbc|?Yb?`VVRI@7Hw)+e}H|dDvYP)TdIfy`AHS1p^7~ z(EHwjS9_aor`VY+^;>XF3=d;(fM;9N(PsCQuaJzzjSG8bUZ$`&K)=s1&AnqV=8;fX zTFS3zslId``i0#$6sozg@!9D`TIHX-7az43{SYJ>_3#Ty`1x|klV5ykVr;5A^LmA| zg(qg#^?ZN7_$GYY!Aox=1(ZG4^OL?d6Pa}Bur)?RT!Y7ZX)&F}mVGs{mNVw(4A9|o z(ncRf%jstqUsJ?g(8taVt8=V0!su-w1tD)K$-j)Ovb2dhe8X>k_2j>7tazs+d^~(w z;s@`}>+2~;ztWCQ6Z)}x;{AEzLcK^eMNJ)b+XQtRSTPT`U_pu0qFkbnfd14=_P0kX z#~#oV{PMyv5}LPKC`DB`+zlkI?Z?65+-6ybf`E4~Uh7?@wDO%!URcK; z9{!Uf&8)pA=@%UK(uOZSvrevNMU;C2JN%cmD7c$kKyC{}*EUS2)yl1M=MsTsHc0?+dpIWdrm4%dV{bvshi5oIk;FS({rC8|1(}t21c}(Ra&wbRg15-1`3Yt!O0!_>&mU0@ z+wNxzGH%p#_x4!Z#_e?VOYDSW5uAosEZfL7nFED^yjD(S}KO>fcU` z_3w3crvKr@s8X^JSn#V9MAs1*9Iho#mnAc(fPY&t5cU;whmR=NX2fAO!;Ps@=fe{B zLxCMil~?F#s`>P5beKz9Wfg=Vf0jgcK3;e4G1Vy8S>{+61cP2zgUCxWxKpX=H2wdZ zasJmCyoL*?PP_WFDw_e*3eO<=ju{x;7rSoHvwXf$(!9#D--yY%iH$cKlYq3dh{9D| zc#6u|r(i&zAH`(zXA4!>vR<{dg##5oKRs#sR9bdsyKi?4Z9tH~{*up|hqpuDYyJgs zaw5Y49^4oVlj#vNr-}TYUkYRvJKU1;qA_14Hl(GCPV1^UUBipt5e`Qburlgmtta~d zPik7?wu1fPgp5hLf=Yv!{GuVBJX*J%TO<$hhe~dM4p;ABLA7m|WHdsCFhCmB|I>eA z42q5m4?p{$J|#N_@8C;52#=|A$iaX-OwWXq)rUt|dYFYzxBGd&!mKtx;r-QPXHpT( zFmg@BxZLE$LxiTX7_m7Z*#}HQA0b*l(4!YyX6#~7@?^uix!jQ06CCJzZHB$BBv&aX zE**w4O4OrVXja&lFPQ#;;keNEc(g?$30>9da2jy&$P#ml$y?Z?ZWKh_IaCY2Fe#%k zRHkpALqr%9mgO+>*3mb>A$BlUEYMYZzi?tPzJOE|@S$c*XWHAttnytjt(#ZhoXFF9 zNja;SbB|;R5ccd1G)va0A!YRjWUMk$uWo&Q`f4Q(!z3de%`KtL=P(w^GC)Rz4gDW( z>R)vC_xVas8KfhBPUz|5MehW^CtF_|F$*_Pb*p|#{f_1Tu=bAOd8KQhaEvx~8ly>L zCv9vSjcwbuZKttq+qP}n_%gG1XV1)Oe|+aUKUdb3XT7(5KXWyJ&_3`lk#4J6Oa*J3 zo6jL5XRrQdZpM#Xik#U_`B_|wG)>eu<4LB(T0nT)J0&AGYcy&^3+Cns1uU0u&a zk!IzYd#FrieJu+0H6HY#Q^_*pus+k*H?V7@@{6qUoZZfDb|4TCUfT~1w%3oJcK7Ui z=RTjqi9k5@0%G0?OUTLhn5=!TXDGr|pK&U+b4&!__jtfW%i>h^Vdd$_|`JPWMBA5VWor_+@;g?6mH1yw!2oubh6i_F9Vfsj1Urwn+e$zMTOzt0{ zB3_P+xp71@?Q{t@9UqngBH8f(xneZY>%|HlxJH!%U7d8vXWdYn9&cB4h_wbRd@?J1 z?Q}xEoW(D&i?dAfDBQrDt>XorGWN8u4%;?)^FSpT`Szlrbujf}e#NH^RtIS7ZLw}^ zku|3i$K0v16Hht=s(spb2`gBS(_)@0om2Rn;xnOP=@e;pO2{y|rjNg>=dV3MTZU zE|23!8M=jS7_2G48WN%1{RGgk>Bqs5yNoXiyGJ5$&vjULx6jRCrK1!4`VAKI;fTo- zW+F2}h$d1_8@teuX-s3S&O9_0_kA3pwa9zSQI;Vv5;G@?dfrh(=2!tVd zHG9jc`$lHg?f&M@ePEz^F#jTrdR?N_eVaI`!ZxYCPJ+jWKPbk_588My?da>L-L;h? zO~tXc+`oQ*WUJ4g2QHs~Zc7>`oKueTLE-)Ca=|E&Gq7z3C#Q~2nU#`>(gj}VWX-pf zHmi*{xMuElgmhzB@&(vLlePbM1Kzix$b+wjLeZ=m_f9%oszfA&tinT-k^!mKnw~#Kx^D>n9O>p^@0%vdYtbw57wbwwLT~UrtgwPc zPfx_I&Oi1Uz%hflKL6ld_}_vAOR8HJqoMw-gL>AanG<@5TurTch~SdrxuC|KtcqPD z#{kFo2Qvu_SfXmE#qXB$VTgNoKt(7n4F8z{CuK_(Aa2e^aB4(9sBjx%S17X83t&kO zD4C}-Q{-99Wl#GvY4bC&gbSDlq5%(mkJF6tG3$kACyx^)ZmGOw`Gkb|A10^|Qj9b<0hcsujw$hLDt zh<6R@=+m(~^V#^2A@0W?m}k?BrArlLETZmP+ln6 zZUCd>vuqdSa{?Wj=I%(1!$Mf`2o)isldZs*LuR*uBTO}ln(S(e!;Jabs8|`1Ihga~ z;#sVkADeO~8alBsdA!c6l@A|~0tBS@XJ*~0i6|SmJct9Cu^!kul{Z-EmU(cpt|hV9 zv$>@zTR*Q!%ANuRbb~*t6}#E4XDV;^oe#pJ>wpsvr=_W*Af|GTA`Fo+Ew=w@WwU9BZmOsl6r9g4D z9l-xBnf=*U^*+^=)^6fH*B*N(`mrjd5U4_6zf}II^`3&*9a>_bGgB*!+1&o{Kvc*k zLiy}`)3^8A3+tWS*13keo}ONtnWGVa0FN-QxSmSugpMRkHK{Bzpl9p*%{Qp_&zy%l zy7Y;9d3zX5r*k%H8`E0iT#gaP7iJDaOQ%7Ja9QvOn3MUECM5lBGL}p-lylGr8e=iY zWLBVp>H7%)Q^Qau#6uL7Y_Opr$THe3N=5PcJpMF2UH3_UqgpM-bQlT2-(-! zdhp+-X-j~Tz1@V@#evA*`_1t1(zVP)K1q)H*O)Xw0Aek??1U!hfV#MFSzH`8K+X zcf|Kj_@G4p1a_}Qj{QXJ?m%UCCmmz$1Ms|}eGBB%jqBU(56NTl#u)x!W#aV&+1=GIBS5xWSv>@7N&%;V zVS#98If%3VSwXY3@0s`ThAGO4Yqf4hSpoY?I5vnfTx- zk`Cwh@6Q7O;MmnKHdaT*oE-&yxw&jCG70AV5Np&;rF3|8l4s6J%yMWMKJRF%V8pb> zBa)x4cY@KAnnC8^jL(B5DGQ+y`!y2RN5ig5Q4@7L)RlG#TljA0=ju-v^mm4+&AiWE zv7>$~=}`Zg>n-^f3p+`lz003ZKBpuFNfo#A7#`sSQ5!y<)KK^b-YW#k`85ol&bua8 zAeR-TiiP)DdV^>nnIya{?x@9V$WB-dZ{Oq60>hhz@Y01R2F^DE82SHmO#B!qmpp#! z9o;Il){RO_$Aj!DB326VrJ6~^M%sKQWKJ@zb$f4Kd`zKWG7BwZFFNHW{ym=3GE)i3^$g7P?}=W= zrU&a)zt&nQ3@xUKPW%&huRrS|8;wvR#yFR1ANAg=9p$uulf(pM6ZDfSnU%O3EcMwyzVDXAA{2n%w7wTI=ciS1(FI8nFg;KDY;zYA z+YC4q%p5eQVN~)?Z@E#uOJ37*UD(w9Nk-5|bB0fvw}FOQCPdWp=~6p8CvHUt_<(O5 zp5R+^09^}gGUH_zAa(>v&o=~|2g!$LO-8eYKB{t!UMdpC+7d?}Xca>KT`-{noQJSm z-!$j3a|y4rDWSzO`S@UPh+`uwE+&x}LgZf7o(S2|KaS=ANYB=#odrWI!>Z)1fbmc5 z^V3G&Gb4DoGJ*-OxfaKvS zyfB_*>(Qzzz~P?98p!;Ke%e`T09K2_7b&iI40v=^tO8!yzI)3!sHm zbNw|n`tQ0;`!~;5v-Dz99&Y#+w!8^W#u#vxDD@{ul7T?TaRrYycWxDvInJqtrM_h6 z)3IR*bg_?OEplPU?f)Ho{`YwRkklYP+@x&g^+l`x-+X!bU&|#8Hg~Fer7++(G^MG^ z3Jwm!Mo$L{NDL5WSMtr8z}~JH!xR4o;y+GYhl7X^Yj94AvL;S&A%e$bXUM4i%2K>D zH(7&+5}bwK*%VXQ`U)77Nx*;pkAM8zL%R{=uxQYcIhdu9SXh&_Rl-z_jp_Z<`$E$% zyL)?%%AKOWCKUkWSK^5GTyJhP8j7#wrDd3oL~xmVe~5d(%GL_?QRe$8P=_|Ng*5*v zmH%XXTu(Xp0Kp{7JL-LUHFi%halU@oh!HS8O8P~!*2deR0IN^E|Efv+OyR$aTh%0u)sg_nLp3@ zPE_u04p}Ct?Cc+27vI!v>Sjhm!%m5dn^Bk>CqKztRe^88PZjrgS-fRP8?^!r2+a0z z_A-Kh4h$S3?Pvez-|+i~kv8UO7bA}m>?VpHu(fqval>W(cDixUf|kzkZJvw^yK z8CDSL&e==dTOO9*^jxdsH)Ly<145;Yy$mu7WcUPKZJt%7+(3fsA>S*tyByL_Eg>G( zAcF)LxIj`tAY^0u72o^eCtm)d+n*ZNqX>js7Y~UqQFMBH8i{b&@Z3=06`Ud~E35R7 zc4$O2uq-7+zfk4d!AUk{BV$T?2mY_QNMi;>>t&V?Tt{fRXMNoxANt-!?rPPdoIz&vm9nwzb0!mb3$X zX^d}aXe@;0xBsB;r@s10qVi8b*a$-2wJr)}eh(yI=cWl?R(Q+way{SYrX*snA)$#> zO{_uZ{K&iK`1pzw0tnM!Z-lS56Azqr7DXwr7eenbM?yjC72k{C^avKJ4ta zrt{l{`a3Ket;wCw*3f>i%wWm6sEQwHo0nVT2@!*Nxqs|ypMWHX|9ABFCrm#DWj$HI z14-JRd8{OSQEKiSglg;OE&%Hu4JCyc&`UC7p2l2g-O$??;DV|NNxX!UexLW1oiI^6IO zUklzd+kd9xe*S`=5A96e9)SwYbFQnLC9ANU2RO27Z|f`JN`1MqhXo0tORZ1(9Ihw* z9o((BFirWPzCDg!5j)#GW&z`@^$suAf~=f z&;TIg0p|HgUXOZH!dXZm2mQ0B;4gsmkE0usnj3_!Q6Zuj|0*_(leu4Nd)UyXX zjWSA~_p=^cR$F5Uqmuk~BV$M-44=NZ=7611;ZyVwbY{O7lDp+X%xdFk&;dAsOk3HJ zleKDdUEOu;wnap|FdNC_$w^}=c$N&=cY(h?`A=M|L*l=ymI3XGxFnjW$!sLS^y2 z0^pC)K6Ia1AEyYtvZPvp2CK_CB|JR4MIu|lb}WXD3snhO7im8J#?9D8pccDTzu&yN zW%f;TmqAJ<+iPT8#?OrAAsT%?w2$&G>w~8b*VS{IWx}OSkt{Sl{Kh5}8$reQ;N&C- z#ee7^0^9G#@eNA6qv;~jDN`4w1w;Ho6Yp2b!PA@xFw0HPu7ibYH zQWc}O;K6W`iHKMirPdVrVF$@n^ma1!WKu~^5ijvj96R*<=GM!W2efag8XD1Ba$|+@ zU?Jn~|P)~{9Kda_VE1-|B62Nw4B+ zr=lh&g2SFD9-EWyJQ>K$s@qHN3b}5@{v0#4G=y{G=DF3i4F6P}17J+dzNII1F$m90 z^0S3mM-g_Jp!9#T<4@59K+u3hqaPt&m6!P#9Kq8FZocG>Yp9hlR|W|5K54B}v-hIH!{A*U@1X4!;oK*dk26v`Qt+1H2dq`$v zbkEJm+1^{jCG17}(aKwpmfW7sxVSMVGixbQ-zT>7x1i7Zd03X;roPq9@~JYGLT_=- zD7Q(TuC`gO&wlRt+DNCO6QK2L`m}#SvRbb3-ZAgoiO7M7V~QmTV`B)A&YrZ!Cey04{`-XuNy#2Cj9orRi?eE55P zRvFAG#}jG9(wYv?;JsgCA0XZ%S>X#ploZvANk@B|QJw;!g+1!0kmoSSQs%1%?ze8j zjV9|i&Gb{t{&NVSD|pd9lQs0CT}F;sx17qUMwGc4F8IxaYFoOI_~iY@-$xqgfI+m8 z*_`yczwHj4~0HIX`i43g> zomwpJI(57{Jy~g8IyXC8S#i2sX=+-zF+E>p-Y~&z_ZZNCLqGrm6c*%G{FEbwZOA|A zb9#4IJ#8d*7eWhuyB6nvSorw%cyyGMR@hcl__z8lHD9|EY3N%;F}>v5Ei&gsP>&1$b71`UKFl9x5m<;s*w^lY~nGx0-cKKzGDEOny-1gqSuESr{ zs4E?l%frWJelCn5tcgk2!zL*++!e6UTvEg!oVge%qH^!d(f*%)`GT2GZ zsHy%sxF#ACjE+MmGmUw>7y&MD=0ZKEd-z_*7Mi(z&CTHv4J&(LRqHqSHX* zuSM?9U1vP^9By~C7{EeATbSRoJYp2BpGv$pps2zlrjhe(t2+)0E0N|6=T^|U$JNA6V7p!Rso{T? zE^wc&XXamIJ7J)}zeMx>btto(4q~EbO5TisV%im{O~7`td8SZdYx)JKRl(`-=&PqQn13hT{*7 zvCfXKL1(P(7T4u0K5M3ijhqn!m%F_@J&r_Q5nKtQDJpCF{e22oX&CfbJal08-W4o}2`qqy< zm~8Nf)RGeZ0{z*>W{cP%91eK(8Lgwmh=d|a^&mHst+<3^iejvi9n{^%Pkai1ml$P; zw4ld#G)gZ0LTgmR9yGtvN$(c>OJ~;Bb4~i&P3Y0eFh0^+cj($md|FAXv=&><51pe$ zkL{$+EUKDmJ>RE6$l%hZhT_&Ge!bDWH3!wrZJ;a0-X(JuxzyyUuOEI!EdP>{J66C~ zV|7%6;%=c0;e8e-BDKItnX;*gYS9j7bn-F){6>b?-rSH?M1zz#)qg+=6 zZB|xx+RvX-Z7T`hSoIx|{&nn6h+4HkLRJ2!y49 zv9N%PzMF|E#x5<`G-Xy+=9_St(?ve{!XuFAV#d2ZUzN@Kwjcf`+`hu&gq2NU!K&9* zp1tkf!Id~#x*S8Xl|+pGYC^v_x`=C_I*~|lj+&m98WYoMd-pnGtQ_Wk{VG;5L(bU9 zD?vd-5W}kE_UU`}y63MM7uRzEE}x3+>N0e*{C!wJ)5yro9v9!jRN-n8A-1Yhky2AU zTexAQNgOjDkw{3*85@7143E(JH}v%Q0(dTxO<3^Fo>wV|NfVo-DN91=@}yODiN*EN zuf2wgl<9c2g`EOZ_O5zXg;mw!%BiM>sV@k{9PwLHxf()+sNIF!!F;(G!tW9gf}Db! z*qAL>9V)1+wLb{umhSkM2%MSHaUR=1O;K6{-jy8DN(*aXvM=f&Sy|;$fYZ|xnRE5k zm01YCUX$5t7%vf%KNZ32C4HL))c=y#KZ*GtEyo$-@l%Jwc`qYQaTBYrsDY*xn8h4J$RvOH37qM4Bm^~tZoGAK!#xe>!$C{@3q zt-2IYb!j-nN;4_f$qu(dZ?&l{X#5gn(*EqB59hnOswUk3Q}9Gb9sueb zNC3h3m%0rAa@f-&FK=%Ud1&U9G~!_PHb$k5{p;6sU);s_sa|GjVbNle3MdS}f{q{J zO4CU1L}181*jL9u+L2qG-DC})d=k2S zd~A#4abBK5;+da3Dy$6>&X=pL(5#RosrcBC93sRrgXS&Ga6@f1AgJL$I*5RZOiSrN;Fj6v8n-8xFCd9HWz9n^>5U zQ;;>G4zY@)1KVyrogN?e5RHw(1W63dok(NP%bq3-EL!4}D+|4kG3F^8b-)C1IF{?%sr^SoAk5Lw0K?Mg#zu;ifR;oYX~ zMO?wLemRVlYrUGmrS~YMX_T)p!mBUq!ilRZy!9-#M6Q2+9o{>-Da%O|zh+e-J&CkQbQ4v((y8ia&?!b8xShpN&igoPPe zsgWXCZu6c^KP#g&NM?M|8myst0-`4VHGMrmz5)>;ZW&W2Qt(G7LMlk$0p!6AgnOQw zz*s9^n7hjjZxhCn3`J>cYfou$uNSu!nQAyJ#y|(~&Vd1cfc>@A*H{H1NBN*b@==#j z0z^U1;zJ~y9^J5zJExA3o{@@;O-dPt&`}ea@Q1>)7JpCZr|Ah0ew9PlT zbrlSgcG?D1u$0lIn#g)1eYtI}S^8riw?X_G9R~FU<7ne!?1twA({)`b)aI>wsoM zLcp)hybChq_}1e_Q92Zr+RQ=4YUO5;w9%r7nkGy@2zAXtLJsA#_F{2p@rW~1iKrD` z;O?N;Sh(70jzIzQQPY^ds`C>=E0o{gVxoVfMaykK@;iEi)k8{FnpsIXN$^BlMs6PB zMQmqHS$urOPhdZ&h0Ks4Iv19r#>hSrmbp2-fe|s0#IAqT@|~v!`!yjHXo3D?0!;?R zE&cGb(-e;`(^l(x)eL2-7=gHfny>9wS)5Raj8zKF+&2zNs7`0z4y>b3SYe%$sDDFT zPM}~ePIzpry|S0*GINzPy=8>fK#aB~DmG=;oNgy0na&vFEM*q@&CMM#gccA@Gcwl` z*PgnxFvN$;qqnl~-8 z+{i8+1MZ+V$ZZ-4O&6nNIY#bp6WBv2Za}}Mln%(gXOMLsu)WF9Vp=`shaWel z5BkB(UoIFv%alSp-`W`0MEIDy)%1aH{hHdIe8iIKPP;o8J(?Tr(w)=O%cq=g!==#R zoe4g_<~>}`99&#jq19&c|2R4bU56hi<42w^=W@z{ZGUH~HA+FgC_c-K9S?{H%X?&Z z>i0CJj&amV8R`>Arqs~LHz(lyhHsRB@JOWKb>0TN-CGuWO|S|BR?eqh4oL7GA6A6R`8p0!V znl;ei3W(p4%>pDOm8Z*RFE{lmCe$15U^6{GX1|NcBq(C25BxNRtaQ(co707Ba2I3_ zH~q&5WZIef+=aC^+OGlP1QHSBJUtSJhKBB!B^lt7jGLfeoZ5?tNg}kg#mGObAyMtF z+1Mc;_`pDlG`u%JIg(LGD2wwK6=Z~h zkW4}pI>2-|KePajrR@+mXelqWz+ZDFIVGT@g^kMjxUc=&8F)`!jeXGTgT&r8%>b;A zFrP@`qH$LFDsFTmTMV9MRDy%zfTgfoHn;SA#vY#*+;54+*SzHu-i!Y!)g(vv_m>x%~HDZ6Loc`(0G1n(Uq z7C^l;S^5T*#9SdEA(5kn)kw%iQ}mf7X0w+!64s{oO^7H#rKk8*vtdkkQ}FEDeh!KO zQQyf7Gl5kxT+Gn^HN^Y#hsOm0m=HAn5#iCu`!sA^mSNU24kOu~7Z#XWd4V}Ft&-FF zI!!>5UW78o6@vW^efJ*_sg2FrY|BF#FW0O)*Kv`pjOx3){UK{rxflD$vM3)X@JPc6k6)F|L02(f&lzKV7bNHtzTLcm2McbpL$u)9LVh zfa9F&b;@NY)4mwEmmG^{EVJ?O86Hi68u ziRO(?Th-aVNWeY*BiddTV}6%_*$(*FjO1{e4al>cTO|AjuO2itHm9O0L&HXYpq+s~^7=#f)z3jV_%-yJ zLD}3&!hw*Khhop5pQRk3ouVNVy4K)Nw%)es7h62AywPf74^x{1*>}}UEaZzS&k@8< zXyo*W#_bj9QOC>j^MMNmR-F3jVG&dt_?wH~CMh^>9FVn4v))jubY;rvMIt>E$+Fa% zx^4y%&zo&Nowcc}$^frIn@WVMnI{O~X~IvOj&@t9Yb%eL;EnOa7K& z3Y8j~lt{VRVgYHAbUm3^4T2g#+UC^Z*nHFca`I1GpVFPy;aTAdr%AEU6|Arfk>9{U7uaItqj;p5*2GtY$%PuvME}n+t`gr9>SZC)Z+2<~(?nI``LXuw+bPHJg$BCC~88dGV z7aCwP^nyjn*P@yjv!$60<;-~!X(^2%pgcIO#Ldt0%us^wmCzIoX6LY15q5iBO!p%f z=U$I2{IQsPWK$nHtsN;t`P!b&qZDR%H)9GTLZA*p>F|X*zX-J#;&9Ye?4P)k8B|#m za6?ae9Je`A&gGqrcF4olDJDb~ns55;VGmBosXh_K&{I+4Mhd2+B8@{bCJh%C zN_lgY;apr_`i)jdew(i{S61_K%KSjMVt;rdF9Lane4xmqyXX6W)hEbm0{VG%ApCR< zYYacrU%c&QaE4Lf6yXP0-)BnILhqwd#-|5lwz;^rY)TfpT3W%-BP@Ah&70E~ z4Cw^c>elV+4VfHM#s@>8ae_4m{uPphoyZeOag@ zp5NZxo?FBV6Z3~<-^jPm%!HQ2^k{*B_k|!Mt(v@DADHwB(~I)OWFYHRxxjLSLeD61`BPxPfU8ZA>zkT+@C=UMlEqH{P(DIXL; zrJLZ4F9pE435foPTxSwc&gekI30IgAS<0AGF1hb}yj1l$f3LTQD&3U$Ich70;I@r; zfaJmmQixwbzlbW{W~{I;m#H(F%y;GumwcJAH`BDm;kEiZlUP+g7zvl)^JX+c%OWW_ zr-+qS$VVY4p5tQJQu0ZbtEROn75lXvos1_1b#+)o zSG-=CgUF;9h&AbzbL!-d%#x?$7IqTNOx8@C#aJo3XN4|miX3PPo#iRmJ-(!)@Mhc6 zW>aeIl~Xojrc)a9)o;o$UI1CO_uV;*cl%rOP{!(KV_sCKd5kp0gHfl7oXvr8Di0Wk2r)OR;SbbF**E7FZSKQ z1joMX^N?z~isi*tm#?j{y*r!z(W0O7*_>EgNrbF`s;jlIolVeO>>$@Jc2ru_I{p0g zIPHnsW__i}p$hzk?V|DIGiz8^*ixRP-31j~eJu&ymHrvred~`|WxKLZ&9<-h#KH+5 ztDQ|{Zu-QgsNjlBHtNir`By$(AknKA$etbprB4zzC})W1Pk3yMetl9l8ROnNE_*A( zIrfoFTpm!vKL4=Sc5_rWh&1NGi_B zZ9!9+ZgyGWHXWQ|WDPkRMa_ATDQaT2fLYq>{&Q+CHtprW+3A>QAhNBg2iU2=ARm7M zG;o|6lO{OlG~V1FgOtRKJ7K9UfIXf zjZH>zSPEzf@xXCL`u?oU%}HtD`HL#%tM*nxF;n;FLYsu|lB!mlSyj5YKwZZa#?mSf zu4SV$nMIyx2^W?c>PGTaW)GF!OQ`J6_4zcmH(_E&QyU~W4D)seoeM)VE3~o9j*9pz zE=9TlC`Rk#8$c751H8I`pG880?=Yq%puByJO8AyO4aCV=m6(osvX(D9A5}>W=;E~6 zPBQ5?h01XceFNS_^>|@eci8YnRk)Z_b3TccSDQG&eI4caqyQ6b^1x4^-4R$5!k}iQ!t$ckt#CB#c0$TK z)R@3XQKjCe+2yfolXY4vJqQSPvDWhp~7lGoVrHM#Etp2Xpy zDaNdRJ2K(Hsf>A><4QL~C*a7V3;)v_MHB!WuA1+A27zSWCC(xv`db8O_JTEa&r{ z?gU9YFFr*LEkw}hdRd3#4`D0Ld8dL@>BlJ-wFAg43V=zq)b244clvp%&p0NQ-e~te zvdNX)Q(0X?D+QBBaO#rnDjC~Qwp(I>BcPkv-R3J(Z7b(=I@YQ_G@ZL2tY>MNPFGX3 z->(Iw6Y5oBcYVq(hlY@|tfe3XSTn2gwS*MPmi8t4`jzlULuJPoRr8JjU4>;t%}D*0 z>!CBDQkUz821hnzwP9J>%N`Wq_2fJU$2|j%Bh&B-c6rsKssKGm$ki3{_vgtLiiOTcU^9! z^j4)see+m6xo=~N%vxD)1AP6)V}*$~W~z~eVK$wPWJ;VLdKij#LL78CsSm8Fi5?La zS?BOeEikT=G0^9#1LS~tZ>c{*HP5GL04Ng;l+XH2RqrDzyc>NMumC@--*1`1HZJl4 zM2b)Us9;`8jk)iSru6CNo++K?d#aZ{pGD(sM_C_YO_|$w!daOlMw`lm^!9*(RZMqD zvg32k?ENyjZmQXd*B)oaLN6wEL&UDM#iPOnn>>yZ%ZdX89-OWVsz_-5QXA*b++cWQ z6P85=ZG2ybV*3r$>YSezWsforCNTsOxuy`XMD`a2M-DK@nUlpP`?n2YOMehiFr=-% zf|A&TyheGJEGS{4bfmPM9nL97Csf5t-}s4#0BlW;$- zxnY4N?D0{{_sHwZ=04rUN~#(>m<$Z>1GcWE4FA|7xr$X|HnteE1ll(idt_Z}c2m)) zSp3bq5^0xC0JhGa;oIk8cLN@$n^?rMojVdPQ9C~Qi_A0xEXBGdFsD>4-&4wo^IEn=rNorgmm5(* zzAN_Rk?$qHuUI(7cpPOpYkoW|I#$ClZ+o%h&M01+QROhRw2ve5Y0UF;e!g|s3mNnV zo~drFDYZN-otvH&C-y1yA`Cr-NKZNcAv}y+Q&Ys`0|ut-avV5)fdV+9P;CHa*Am## z4kaw0X&7oN7TVU)&)U9*Aq7js$$*Eb{##h=G)c27l*N#y-6%tN^0OWLjYFmW3XAz(-D3_R z@7eP8d2q$dTrg!y8P@TJ`bEUflU3Cg57qbT2om&zj+KY=k#viX%%8Uxk?fymH;0eD zT~7!+;kd>W9jE|vcZuJ0%vlr$MvaoKD>oQ{I-Z^joEj#}m#!_Us$Re5$thzi;ou-z zHfe4u^CRG^ui7mxJLc_AV&s^8b52u9kC0T6WR`!@i^rM$fz~_3Rt2W&lCncd8yMqr zK~L7mHqz2Cr?Sd;kc%IX_9Id%$->UxQ6G4fl3y+D$Q4;W zu?zyi!0Q;9v0itM@E~Yg?~a68!*W@Oq=LCqG*jlnnnZm8{cX}Uxty=+m%Ta(Y50z| z%1q(YVVHYND;hWGk6emw}AC}%yFtl3fstYFrs)+$dn*M$2A+6nq;j4n7 zG=3z@H&1&W1OjU*lIl5s=o$PeCbj38F~)DIZrkSSZ=->1gdM~3Q3{P^>8|NUDRy-< z6F$sNT}*FwZ>uw{Gkb-?At>m0cefXQ$n&_HyN8$vBg-c_W|XTP2NLW%w{!8YYUwWP z{;*|ZuXhr-bjFjc?5U}$8sG5Vn}d|KX{hNqH!?m;>#c=ONYxEEPMEOXp{KNJis&WC zOP2e$K90x?Q-I`JL0|w^XGi#?s2XL-GOJlXaKCKdiC@nWE|#qecmJ8=SRWDqSVk*n zk10YuCLSDKSnr`mCoEKzU`s*Xg)ZV6Yf)XHCA8}DPd1h94I|bHp&ghn`Pj$e^zJMm zQlie>IqGE-fmf%qQ9Z|2+mYvmo|!xn!#sb=bdBvri&WPyH3~*3-|L4Xt(%jpLN7wF zNfC>`kFzTAz9@hrV6rHe&;0VPh>i?0O;H79qwQGk9}J4yMzlK|$+^YwbqCy{bY#@fgZ%x#}?ji|yF zD|9{IsU7H(1u4<^;E}|)2mhX%;W|n>I;cyey?9vV{b2b)8AL|D?EAIEui${WfTrkRkq4&Yh`Yr0%3H8&WB`~WU8J_|tx0e?Y*z}T$`m?#J9_O@LmMWdG zI*3I#sg=p4XewS0Jv?7qOG`&LHa5C-j_e?#AB%5AS89l}65Wx#>|#H`16=$t#7M{8 zK8I?X8)0AoP*UoviRK&83)-LME}Zr)k7zwux&tRm=6KzL*AsTJ?^$v`Rcf*|eNWJy z-)G32wOOaW|1GtMk*G{aC}z-g6F6Y@V~?1RQD+Svc^Hi`4a~VQd3O^YeaNX+tDg#D zEErt5+CebvDwRnkuTxG*&?GjC6=Ym8#Z(!wLpfht^R$L3pYmZ)YST(;A{dEGM0bq8 zCJiY^0r2fygij4=O!hRoN_IJ#Z@EH(C6@CxQDa=4SV$Ib@4U3~YQ$xGD=NWKQ?pls zUOQ%WziDA}qsdL_u+O?oRJ}rU?xhn`4eM0x!u&e^$jb^+i{26swyrKJRc(3W!kFW- z#4*FcLeb`0WHuoDGuhF7?LeIim*xi*45C4;$FktzmPcod>}P-!uQ?u3j^f8uYzjNi zqxxBA!R^#0A3^jY*)NSllQtT_&j%gj)&{evNs9JTrL;0=_1aq>+a6YD^Xbam;aVMAV29a0iVS>cQCiG(JhFcq-O&Wj zptn9*-C14jt>js9zm!jwHM@vd;B0>mt8;f--$Be8TCRn!c5#{1HXxuKk~x=ZYHaLO z!^kSB(=A0bRvO9bg4;53RSwEeI~|=B?#b7mlRT`bC9 zmY&SU$TB7?WFlgZKyb?>z*pmA!fg5>v{H!z@_=Pho2Nt9x^*XUj0|#-iG|IS+h_K4 zbj&%x8g*aAh}rbbq|vAxZ+}s??6K2&W>$(~F4Zsgg;7+8y6UOU{aWRXZa+n zbAFweHXs1NxcuxBjeu|R_A$_@1v@RHw}&|GOHCvGeuw6f7Tm*jZtMqQP~C)t8S~A% zqJ6HZdmk-d6=C%Cr*Cp$UUhxhs?ahEy1Au`kG03gGs@YNzyfO}ma8N6jK_t@Xaj~` ziO}O7^D^L@DLX0a+(0Xf*!RHDZ4R_-y<@SA;VOYsw^x$I2)?scGvVCW73cBduUNDXq># z6v;@xiambaMB|2lh9tovxTsMPBDBm3jQCts;*1_8E74IPQ8v5lvK4q!Q8AFiAS3V6 zT(SFo?(qKFRPB@|snWWf)W43XySCo2hx}Maa~1lBrW?rFkS@ar$e7|rM&b>XN$NIr zVKr9N2V4R2wF^NRjKgAVzr95yT`eq1l&ctHf==^ASA^2_ z16cI}i2+%baJyJA7gLGRb&q>tZc|;`6~B;}6jhsQ<(USjB2c>ZOmMT-+a%4c!RnBd z5)v*ocZW09I;gG==#JgoKDimS=(HBnSFAv(p{7c^Sc@Jieh;8IuzC{m+%sBLECX&N z4uHIfj$F)W(B)iV`J+OCA&Ba43Zt~9K4U{vZbyFKjjm2|!9SUn28&zdxKx@X7fp1} zGhPhQK19=#?KI;Mq}5n|1VYEd)VrpPyk0?@SxMNP1DM(PvFe(NMY5!>a>{$(e(yF` zcgH9j6Ldm&D2zb-b{!hsy7sgc{Pecdj{4?I8$QrJFLsIYEm~IJ9u~O6v9V3Vj3IHj z#PBR2JXlOzn7oWMn(PH45pg((4)(6oPjY?5;LIRt`0I&!RqaJ%+yUQys;pz8)DL}g zi>ajhhxPcnn%w>QZA$s|=u~Eqx?IKU54qVif7a0N5#YvQ!#a2*OYwW$i@nYZ&j_t) z9sz;BhMB8T!79R!@_hids5&vEc+$Gm6Me$iOGu{peDqjmA1I0IVSWS*+4dR)4d=p-0IYG zn+k7l@+s030Nuae&ZN2kc_Pz0%_Q5A5lCh;0ZiShPza2o(?O|sTTLhAHQkFba#AfZ zI4Js_X}+<<^P`6zSG*7$^&w7AgBy6w$s@WSw^w_+8G3szqk8ExcXvNDiq_{fv4a>e z)8paZ3rZyDiGg~vF?tfb>%P{NTE!#5h|g27p;iz2hKtNy-1!)1H_68CstA&zr`zp} zuySMji84GN?0W=Bd!aDioSYn!h{}p+l3y}^aLr;ha1A5Bm9C;6bQV2@e7(2cp8|~@ zj3CL})o+IM`g*lsaEb_oEsmIRpJTyT6$w3exM;Z3Thg>8XQ6v=AG=hW3(B}wod3zA z;AMR`XJ=F`Ev>NsUiYc(RT5&sz2!Oy7~YNR3Z{!6!>qKF3(&|xNO-LsS0!eB{CqdJ zH`D7m4cnAgCnj{)@6Zos(RO9D?|UyePgFdqiS22xPOL){sku>}P)&UFtFV;i#mVi4 zWl&(G59Lzxuy+Pe)|Pi{hIbrX?xnUr<($y$@j`m?F>-o?R-)WDdUmu;j6SGmGI6Eq zo!FVdfa$MmuZ5QwAV6=y2f zB#e=sQhgwj&-lL3ofYgn7PtVj#`r>u65=wSB$Lsbm(mQcbUJ0>eqMjT%o+9zkX`;ljwRf$Vt7DGRrWqZdSd#4SJ2ve zOGpXfJB)A6*OHoY;jF{p*rL{Qb{0J48@-CgWkO?W$Zf7xapjL#UlhABkc#;M$3R4v z@5@{eUh4tu2g&6tYCo6Gn${g|s3FD2eV`$;igGa^bLjxpDk5+?LQx~yu!Zl1NH!Lb zz@Z3StF~SNKB{GF{G9l5lNf4MN@)iynyUZx%OFH_c1) z-;92bgyPr&me#i`^AZMIvHN6P;=N+jxc<2TAL2IkTOcO0*hEnx2OMExt!76-<7vh& zeJgI(Zja!ix-y9`g3)e)7aMRABAwH2Zh9ph?Z+&}Pp0o{BteK4#TMIp|G&1*I;xH( zd)o;vL4&&!+&#Dj4^D9RgZshV3GVLh?he5n4({&mKkl7%XXg9X^s0Zm*6Ql2?!D`M z-d&5WY+PsPHj<2g(^L=B$VJ~*i;fq1#}asJI^$uyVohds&o9PuZ!L|M#MMT%lrEs7 z8A)vH6kUszsyq~eC1JJJOO~Byhey7;*P|Pu6Op{c8(I&sYvFso`_lx^A(0}gcvVS~ zgH%yo9rxmB$-3roJdIf~*LL5^v?PwLiOArSs5zzZI{MS}4b53>rVAf{85P}_M0q&$gMQt{{YEGs{mD&AgDRFTv5RJUCUo4)@3g*tN6~+W|w&xpJErR}j(PV7od6yusUemH zfvK3087=*R*_o+TZmK=zdu4^UcT>($YnHiW{-h`>HpH1#Q&5)G#N^~vXaRG%4Frq< z0`#et&~0EJV3r(Gv*mgl)p@i@7ID{SnwltO=FgrMdJM_cdoNQ$QM|`@1!c?d*wo|g z9!-llwQeI(H4R-mxii*tj_M|#IGYhN-yaMf-^Aznn$9ib4ekHzX@yJXR2~k*`?{sv zZRlJM9M91;hjf=ahf-2@R*Axg&6pM@{7}0cyY@R@@MbJEx@)zDL3l26#i9TCEpISt zN1^#stoiHJT1EM=it>slOPX2uEnVk--mevlP1GF^4bo?`Ih}& zT0;Rkl@j1`^XZveqNMTeqLA`wBhlmu5Xw@V(?V||y6E!Dqy2X2HM}X!gVf1NZv7EC zH;beb`OHrDtHv(Zs+=15Ke4{KQ1|5&?9t`Hu47uvhM2L47b5#0{#^p!q4T} zVChFl^WDz=LI$XQo3Pfo>-GD4t=y-Mc6gECkKjJ>uw?kI1p>BWwkWHaz0-ZCDT;)W zlyV`jS$RGVn^X@k#aAQf=i`g*C7B*9tIalvuBY%I`KnER_i&Vrh8Ql?zyNFBhi2vt zmm%=mipYqaNp9b?_-V8AxphzEg&9)POC?-;DyolU{JOtovT)*|k-Wq^vv6)xo(}IVH24bVIrT4u#yuJ4 zW(Aw`^<0#lEmVszrHu-%04C#gqLf|YPWF)$%)IKUo*%!s-1J24>%Ni>u=lA$YO^v= zW-z85>H3+HQU53n4;}6n9$K-li)yp7me+3|lQ8XhmKL(P0u(uz>!fC+>)MI}^?qRv zp8=lR29O#Eq@mE^EAz6ogK3bqN*b_8<6nT2clk^!`-u4lW1+!-B zr%!*4=U!Po`XaXgMmnf&)w&R0JZcQP&5O!ImLVulomibMCKKfX#k7FF&;IEXv73a5 zpi(yW)+{lK-S#JEc~N^QUS%JG(Cj-WH6h~f#<_yuAekcPLPH;(pTBPNsvi&ZTq9%7 zag7W?>udR{oV)f-aA#2lM*$VN;i9yQQ||&{21|rUNwS`;<{L-fuEvD>su3)YnAKGU zTX??Sz}ILqQ2jEG!^?jqIhZm@bG#tBJZWn4zQCCKii2_rasG~rYcTNan8PwafB%5{)Il)0SprN`dyZ3zV3m08DJ~e^5|BVFQGy5FWcTbgq zPrU4Ln;5qzj#AU;vL!>ZtRo*SMoBr~u@G2T?* z->2al>b96eoWvMaaXeMc>#Gxj(>lCJ?O<*DatHa+BV&R0LsF%Cv$RPHx-b?KgxaoU zNhFzUGD)3*K7I9gL0EE4ceZj#k%`HR2hZPs1%d#r*_JYImzextRbW-pVMv*bLtvVW zcc^BPsaRIBX?P5p98-AOqrfbJeduJX7SlZ~q?e>yEGeHs4UGk{Mhy&J{mZkf zZCRPGXU*7Q%QrnE?h7APFnxZU&suw0B&zGDV+bO}r*XT%&6Cp&FyStU?QpcC(ZgLA z#85G{beGQmWDbhf7_DF96f?v z(t@gS=p3JH&8Vi2^dMvjT_?o$|0;Bzu=)WXZEhL6>lPNA)WBun83zi$*iMZ!g zBc{gwFSu?c6(Zqa;c>0(gja8F)y|2w+M;(nCKd?Bg!7LJZ-bAF5qBZ5-MpK*?n9~^ zHuW8w)6&!=N4=yoJ_%vadI8hIl`_FH{|_RG;nOwE!zse9s0q<3*x!?eGn2tY7b#fu z4~W*4=<}%+^6AAKtm6v@hqLdJhc6dKPUu{fTING*ry803KXk26J}G1MmMH;b)tgVg zgAHwjg~6F8!pY2Nu4@*||DpqZT7|oe7x_%&pgG|ZMq7#P9rab>8MbWCzk{jwNoG_3 z1J->&j0>M`dG%pa{&~v31h8gQWY_x_P37R-MC<`fU~hSHf#sr$TVGiZ=RY_8zf9u> zA1ULtQe=K|Q*{x%0vaI7*KAbfjCEKWmA$N3#`y(Ar?3-iCxzK5ExpMt!# z&Y@fIpcb%N z7mXnBrLR^Gm^ot%SR7$3bQ#xo{jt}(e5Fr0nDiI$mA)Ve`W6J@G^0oi(y?)Cxf#HA z8|nqRf@->Jt`RYsei%mw(W&QX-{zWdptWDWOTwVkYi56~AT1rOL0Fz?Teg}#roR_^ zc_8dStWjmfP{DnVouQRZZ+A@-$|gs=u~)F{;_y7Jh&aq%JDbBq@=%yHjXio$Qc_`^ z_Q^=&g=Qy*uEBP2rtf@_Aodp}HR>M8aCebP!ey~46hCWw8|+P=tux-pLeoP%jK4g{ z)mWA#beL4~S9nQU65f-db5z5w)>n*!53?;E5qkaiAbvts;|7Za#z!iOmd6ySx*spC zG}xcxx33KtdnZ}*r5JJz;=Fj7=V*Z;}!Pv<4xz92|q`7RVBX*cZK)wPjDG;Y<`ripu3 zXlYX(71PiE=%8JceLI?ct$N%FE<}<`0tV%%*%BeRbxBI$!bAs45AnxvN}gplbj*M~ zv~(-c~e^T%+VB?wF|H#sT0}VmYLUVcd5yXO@OGb(WK_@`lyglv$>QyI1EQ; zo0S=>4-Qvcd@URAAj-bE7;i{w%!~dpx6xuV!ehBLxUKMMEGYyP)_G89f*~@HPM1(; z?XT0ZTb)f`Qjz!HAq24c)gw44-3VBga4-03+ zg(3&(|`r);+S~JZ@na#d=;vz~_Jo(nvXGb$pJY21=O+2_`EJ(+kJ$WlEeDp4Px$-QG z1ESoBqgs8Uhk^%z;`pUnH?z&l64IE_K$mZULAPO6Sm%OQ{)Hq=#BZ^r;vY4qH0jsx zHwfexpe&#?)mzsh4*j|V2lkdU_!t%f@o!(|G3#mR!^z-eJzVNDl2<45&3yS`e!Q|| zEG2^G%0~W37rkvwZsP2}BJB=9GzlwMC~t1&CU!qZcQ`HV%?;E0cc|{06O_ScQc1~xEeuuj@xmRhf|Xf&n_6sAKxa% zm;?yWe6)#AT!FY46wQ&ji>*K7Gy@ZrESJOE!Q|=Fe^ z4_u^7^(}uc=O??J)Oo(9{NB(VLc>F=vpP))drUN$9L1$lvuB2cKE zCdZan)5%(vHGtwtZ%>Ow1aE29;BDxPc&tYsT3cOd4^=9!>?=E#PdJ)tcQdR}2@aMAM?qw#prwDkmV4rKqfo zsgu`Y#z?|GJ^jIpP#!hgC=`3VDrxuW-v72z8uccwQZc30fUF?-{5iZEf(f)Xxxym} ze;sC~?O*Z;5g?BGWCie2eP>+HgA8<4Ok9-5fQ5cw6QFaKG**{HVir0VQ&KXNRw6Uq zn9p9sN^az8?5G}Ii^L7v10~_H%f=C)9Uv*znjaTHjaq^g|88}Kqz_|D=W@%-Z4JrV z<~~Eya=Q*ZE?Jc;3yWf)r@dCI1-*9O(xI9ie>NpcxpIXn>Pl#6mdf8}MJh*@Z%#exQ zt((=vO3aY-A8{r}6OCfjiodjEzE_qNl+-ZDqSaVP`$t=i%d%#VzJj9cO?_=uS3B&s z)Hbt(6?v4S!5Y5qLY2P4O3qB0103s`b1_opWg6pK33M@>P3jj1=K2WSzjz|?! z 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 diff --git a/platform/android/build.gradle b/platform/android/build.gradle deleted file mode 100644 index 607958a8a..000000000 --- a/platform/android/build.gradle +++ /dev/null @@ -1,38 +0,0 @@ -buildscript { - apply from: "${rootDir}/gradle/dependencies.gradle" - - repositories { - google() - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - classpath dependenciesList.licensesPlugin - classpath dependenciesList.kotlinPlugin - classpath dependenciesList.bintrayPlugin - classpath dependenciesList.artifactoryPlugin - classpath dependenciesList.androidPublishPlugin - classpath dependenciesList.jacocoPlugin - } -} - -allprojects { - repositories { - mavenCentral() - google() - jcenter() - // For publishing Maps SDK files to Bintray - maven { url 'https://mapbox.bintray.com/mapbox' } - // Snapshot repository - // maven { url 'https://oss.jfrog.org/artifactory/oss-snapshot-local/' } - } - apply from: "${rootDir}/gradle/ktlint.gradle" -} - -subprojects { - apply from: "${rootDir}/gradle/dependencies.gradle" -} - -// Load build system information. If this file does not exist, run -// `make platform/android/gradle/configuration.gradle` -apply from: "${rootDir}/gradle/configuration.gradle" \ No newline at end of file diff --git a/platform/android/checkstyle.xml b/platform/android/checkstyle.xml deleted file mode 100644 index c73ada156..000000000 --- a/platform/android/checkstyle.xml +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/platform/android/config.cmake b/platform/android/config.cmake deleted file mode 100644 index 98d18ca32..000000000 --- a/platform/android/config.cmake +++ /dev/null @@ -1,143 +0,0 @@ -set(USE_GLES2 ON) - -include(cmake/sqlite.cmake) - -# Build thin archives. -set(CMAKE_CXX_ARCHIVE_CREATE " cruT ") -set(CMAKE_C_ARCHIVE_CREATE " cruT ") -set(CMAKE_CXX_ARCHIVE_APPEND " ruT ") -set(CMAKE_C_ARCHIVE_APPEND " ruT ") - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections") - -if ((ANDROID_ABI STREQUAL "armeabi-v7a") OR (ANDROID_ABI STREQUAL "arm64-v8a") OR - (ANDROID_ABI STREQUAL "x86") OR (ANDROID_ABI STREQUAL "x86_64")) - # Use Identical Code Folding on platforms that support the gold linker. - set(CMAKE_EXE_LINKER_FLAGS "-fuse-ld=gold -Wl,--icf=safe ${CMAKE_EXE_LINKER_FLAGS}") - set(CMAKE_SHARED_LINKER_FLAGS "-fuse-ld=gold -Wl,--icf=safe ${CMAKE_SHARED_LINKER_FLAGS}") -endif() - -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script") -set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/version-script") - -# Use LTO in Release builds. Due to a toolchain issue, -O2 is also required for the link step (https://github.com/android-ndk/ndk/issues/721) -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -flto") -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -flto") -set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -flto") -set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -flto") -set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -flto -O2") -set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -flto -O2") -set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} -flto -O2") -set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO} -flto -O2") - -## mbgl core ## - -macro(mbgl_platform_core) - # Modify platform/android/core-files.json to change the source files for this target. - target_sources_from_file(mbgl-core PRIVATE platform/android/core-files.json) - - target_include_directories(mbgl-core - PUBLIC platform/default/include - PRIVATE platform/android/include - ) - - target_link_libraries(mbgl-core - PUBLIC Mapbox::Base::jni.hpp - PUBLIC -llog - PUBLIC -landroid - PUBLIC -ljnigraphics - PUBLIC -lEGL - PUBLIC -lGLESv2 - PUBLIC -latomic - PUBLIC -lz - ) -endmacro() - - -macro(mbgl_filesource) - # Modify platform/android/filesource-files.json to change the source files for this target. - target_sources_from_file(mbgl-filesource PRIVATE platform/android/filesource-files.json) - - target_link_libraries(mbgl-filesource - PUBLIC sqlite - PUBLIC Mapbox::Base::jni.hpp - PUBLIC -llog - PUBLIC -landroid - PUBLIC -latomic - ) -endmacro() - -## Shared library - -add_library(mapbox-gl SHARED - platform/android/src/main.cpp -) - -target_include_directories(mapbox-gl - PRIVATE src -) - -target_link_libraries(mapbox-gl - PRIVATE mbgl-core - PRIVATE mbgl-filesource -) - -## Test executable ## -macro(mbgl_platform_test) - target_sources(mbgl-test - PRIVATE platform/android/src/test/test_runner.cpp - PRIVATE platform/android/src/test/runtime.cpp - ) - - target_include_directories(mbgl-test - PRIVATE platform/android/include - ) - - set_target_properties(mbgl-test - PROPERTIES - LINK_FLAGS - "-fPIE -pie \ - -Wl,--export-dynamic \ - -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/src/test/version-script") - - target_link_libraries(mbgl-test - PRIVATE mbgl-filesource - ) -endmacro() - -## Benchmark ## -macro(mbgl_platform_benchmark) - target_sources(mbgl-benchmark - PRIVATE platform/android/src/test/benchmark_runner.cpp - PRIVATE platform/android/src/test/runtime.cpp - ) - - set_target_properties(mbgl-benchmark - PROPERTIES - LINK_FLAGS - "-fPIE -pie \ - -Wl,--export-dynamic \ - -Wl,--version-script=${CMAKE_SOURCE_DIR}/platform/android/src/test/version-script") - - target_link_libraries(mbgl-benchmark - PRIVATE mbgl-filesource - ) -endmacro() - - -## Custom layer example ## - -add_library(example-custom-layer SHARED - platform/android/src/example_custom_layer.cpp -) - -target_include_directories(example-custom-layer - PRIVATE include -) - -target_link_libraries(example-custom-layer PRIVATE - mapbox-base - -llog - -lGLESv2 -) diff --git a/platform/android/core-files.json b/platform/android/core-files.json deleted file mode 100644 index e21a586e2..000000000 --- a/platform/android/core-files.json +++ /dev/null @@ -1,195 +0,0 @@ -{ - "//": "This file can be edited manually and is the canonical source.", - "sources": [ - "platform/android/src/main.cpp", - "platform/android/src/android_renderer_backend.cpp", - "platform/android/src/android_renderer_frontend.cpp", - "platform/android/src/annotation/marker.cpp", - "platform/android/src/annotation/polygon.cpp", - "platform/android/src/annotation/polyline.cpp", - "platform/android/src/async_task.cpp", - "platform/android/src/attach_env.cpp", - "platform/android/src/bitmap.cpp", - "platform/android/src/bitmap_factory.cpp", - "platform/android/src/conversion/collection.cpp", - "platform/android/src/conversion/color.cpp", - "platform/android/src/conversion/constant.cpp", - "platform/android/src/geojson/feature.cpp", - "platform/android/src/geojson/feature_collection.cpp", - "platform/android/src/geojson/geometry.cpp", - "platform/android/src/geojson/geometry_collection.cpp", - "platform/android/src/geojson/line_string.cpp", - "platform/android/src/geojson/multi_line_string.cpp", - "platform/android/src/geojson/multi_point.cpp", - "platform/android/src/geojson/multi_polygon.cpp", - "platform/android/src/geojson/point.cpp", - "platform/android/src/geojson/polygon.cpp", - "platform/android/src/geometry/lat_lng.cpp", - "platform/android/src/geometry/lat_lng_bounds.cpp", - "platform/android/src/geometry/lat_lng_quad.cpp", - "platform/android/src/geometry/projected_meters.cpp", - "platform/android/src/graphics/pointf.cpp", - "platform/android/src/graphics/rectf.cpp", - "platform/android/src/gson/json_array.cpp", - "platform/android/src/gson/json_element.cpp", - "platform/android/src/gson/json_object.cpp", - "platform/android/src/gson/json_primitive.cpp", - "platform/android/src/i18n/collator.cpp", - "platform/android/src/i18n/number_format.cpp", - "platform/android/src/image.cpp", - "platform/android/src/java/util.cpp", - "platform/android/src/java_types.cpp", - "platform/android/src/jni.cpp", - "platform/android/src/jni_native.cpp", - "platform/android/src/logger.cpp", - "platform/android/src/logging_android.cpp", - "platform/android/src/mapbox.cpp", - "platform/android/src/map/camera_position.cpp", - "platform/android/src/map/image.cpp", - "platform/android/src/map_renderer.cpp", - "platform/android/src/map_renderer_runnable.cpp", - "platform/android/src/native_map_view.cpp", - "platform/android/src/run_loop.cpp", - "platform/android/src/snapshotter/map_snapshot.cpp", - "platform/android/src/snapshotter/map_snapshotter.cpp", - "platform/android/src/string_util.cpp", - "platform/android/src/style/conversion/filter.cpp", - "platform/android/src/style/conversion/position.cpp", - "platform/android/src/style/conversion/transition_options.cpp", - "platform/android/src/style/conversion/url_or_tileset.cpp", - "platform/android/src/style/formatted.cpp", - "platform/android/src/style/formatted_section.cpp", - "platform/android/src/style/layers/background_layer.cpp", - "platform/android/src/style/layers/circle_layer.cpp", - "platform/android/src/style/layers/custom_layer.cpp", - "platform/android/src/style/layers/fill_extrusion_layer.cpp", - "platform/android/src/style/layers/fill_layer.cpp", - "platform/android/src/style/layers/heatmap_layer.cpp", - "platform/android/src/style/layers/hillshade_layer.cpp", - "platform/android/src/style/layers/layer.cpp", - "platform/android/src/style/layers/layer_manager.cpp", - "platform/android/src/style/layers/line_layer.cpp", - "platform/android/src/style/layers/raster_layer.cpp", - "platform/android/src/style/layers/symbol_layer.cpp", - "platform/android/src/style/light.cpp", - "platform/android/src/style/position.cpp", - "platform/android/src/style/sources/custom_geometry_source.cpp", - "platform/android/src/style/sources/geojson_source.cpp", - "platform/android/src/style/sources/image_source.cpp", - "platform/android/src/style/sources/raster_dem_source.cpp", - "platform/android/src/style/sources/raster_source.cpp", - "platform/android/src/style/sources/source.cpp", - "platform/android/src/style/sources/unknown_source.cpp", - "platform/android/src/style/sources/vector_source.cpp", - "platform/android/src/style/transition_options.cpp", - "platform/android/src/style/value.cpp", - "platform/android/src/text/local_glyph_rasterizer.cpp", - "platform/android/src/gl_functions.cpp", - "platform/android/src/thread.cpp", - "platform/android/src/timer.cpp", - "platform/default/src/mbgl/gfx/headless_backend.cpp", - "platform/default/src/mbgl/gfx/headless_frontend.cpp", - "platform/default/src/mbgl/gl/headless_backend.cpp", - "platform/default/src/mbgl/map/map_snapshotter.cpp", - "platform/default/src/mbgl/text/bidi.cpp", - "platform/default/src/mbgl/util/compression.cpp", - "platform/default/src/mbgl/util/monotonic_timer.cpp", - "platform/default/src/mbgl/util/png_writer.cpp", - "platform/default/src/mbgl/util/thread_local.cpp", - "platform/default/src/mbgl/util/utf.cpp", - "platform/linux/src/headless_backend_egl.cpp" - ], - "public_headers": { - "jni/string_conversion.hpp": "platform/default/include/jni/string_conversion.hpp", - "mbgl/gfx/headless_backend.hpp": "platform/default/include/mbgl/gfx/headless_backend.hpp", - "mbgl/gfx/headless_frontend.hpp": "platform/default/include/mbgl/gfx/headless_frontend.hpp", - "mbgl/gl/headless_backend.hpp": "platform/default/include/mbgl/gl/headless_backend.hpp", - "mbgl/map/map_snapshotter.hpp": "platform/default/include/mbgl/map/map_snapshotter.hpp" - }, - "private_headers": { - "android_renderer_backend.hpp": "platform/android/src/android_renderer_backend.hpp", - "android_renderer_frontend.hpp": "platform/android/src/android_renderer_frontend.hpp", - "annotation/marker.hpp": "platform/android/src/annotation/marker.hpp", - "annotation/multi_point.hpp": "platform/android/src/annotation/multi_point.hpp", - "annotation/polygon.hpp": "platform/android/src/annotation/polygon.hpp", - "annotation/polyline.hpp": "platform/android/src/annotation/polyline.hpp", - "attach_env.hpp": "platform/android/src/attach_env.hpp", - "bitmap.hpp": "platform/android/src/bitmap.hpp", - "bitmap_factory.hpp": "platform/android/src/bitmap_factory.hpp", - "conversion/collection.hpp": "platform/android/src/conversion/collection.hpp", - "conversion/color.hpp": "platform/android/src/conversion/color.hpp", - "conversion/constant.hpp": "platform/android/src/conversion/constant.hpp", - "conversion/conversion.hpp": "platform/android/src/conversion/conversion.hpp", - "geojson/feature.hpp": "platform/android/src/geojson/feature.hpp", - "geojson/feature_collection.hpp": "platform/android/src/geojson/feature_collection.hpp", - "geojson/geometry.hpp": "platform/android/src/geojson/geometry.hpp", - "geojson/geometry_collection.hpp": "platform/android/src/geojson/geometry_collection.hpp", - "geojson/line_string.hpp": "platform/android/src/geojson/line_string.hpp", - "geojson/multi_line_string.hpp": "platform/android/src/geojson/multi_line_string.hpp", - "geojson/multi_point.hpp": "platform/android/src/geojson/multi_point.hpp", - "geojson/multi_polygon.hpp": "platform/android/src/geojson/multi_polygon.hpp", - "geojson/point.hpp": "platform/android/src/geojson/point.hpp", - "geojson/polygon.hpp": "platform/android/src/geojson/polygon.hpp", - "geojson/util.hpp": "platform/android/src/geojson/util.hpp", - "geometry/lat_lng.hpp": "platform/android/src/geometry/lat_lng.hpp", - "geometry/lat_lng_bounds.hpp": "platform/android/src/geometry/lat_lng_bounds.hpp", - "geometry/lat_lng_quad.hpp": "platform/android/src/geometry/lat_lng_quad.hpp", - "geometry/projected_meters.hpp": "platform/android/src/geometry/projected_meters.hpp", - "graphics/pointf.hpp": "platform/android/src/graphics/pointf.hpp", - "graphics/rectf.hpp": "platform/android/src/graphics/rectf.hpp", - "gson/json_array.hpp": "platform/android/src/gson/json_array.hpp", - "gson/json_element.hpp": "platform/android/src/gson/json_element.hpp", - "gson/json_object.hpp": "platform/android/src/gson/json_object.hpp", - "gson/json_primitive.hpp": "platform/android/src/gson/json_primitive.hpp", - "java/util.hpp": "platform/android/src/java/util.hpp", - "java_types.hpp": "platform/android/src/java_types.hpp", - "jni.hpp": "platform/android/src/jni.hpp", - "jni_native.hpp": "platform/android/src/jni_native.hpp", - "i18n/collator_jni.hpp": "platform/android/src/i18n/collator_jni.hpp", - "i18n/number_format_jni.hpp": "platform/android/src/i18n/number_format_jni.hpp", - "logger.hpp": "platform/android/src/logger.hpp", - "mapbox.hpp": "platform/android/src/mapbox.hpp", - "map/camera_position.hpp": "platform/android/src/map/camera_position.hpp", - "map/image.hpp": "platform/android/src/map/image.hpp", - "map_renderer.hpp": "platform/android/src/map_renderer.hpp", - "map_renderer_runnable.hpp": "platform/android/src/map_renderer_runnable.hpp", - "native_map_view.hpp": "platform/android/src/native_map_view.hpp", - "run_loop_impl.hpp": "platform/android/src/run_loop_impl.hpp", - "snapshotter/map_snapshot.hpp": "platform/android/src/snapshotter/map_snapshot.hpp", - "snapshotter/map_snapshotter.hpp": "platform/android/src/snapshotter/map_snapshotter.hpp", - "style/android_conversion.hpp": "platform/android/src/style/android_conversion.hpp", - "style/conversion/filter.hpp": "platform/android/src/style/conversion/filter.hpp", - "style/conversion/position.hpp": "platform/android/src/style/conversion/position.hpp", - "style/conversion/property_expression.hpp": "platform/android/src/style/conversion/property_expression.hpp", - "style/conversion/property_value.hpp": "platform/android/src/style/conversion/property_value.hpp", - "style/conversion/transition_options.hpp": "platform/android/src/style/conversion/transition_options.hpp", - "style/conversion/url_or_tileset.hpp": "platform/android/src/style/conversion/url_or_tileset.hpp", - "style/formatted.hpp": "platform/android/src/style/formatted.hpp", - "style/formatted_section.hpp": "platform/android/src/style/formatted_section.hpp", - "style/layers/background_layer.hpp": "platform/android/src/style/layers/background_layer.hpp", - "style/layers/circle_layer.hpp": "platform/android/src/style/layers/circle_layer.hpp", - "style/layers/custom_layer.hpp": "platform/android/src/style/layers/custom_layer.hpp", - "style/layers/fill_extrusion_layer.hpp": "platform/android/src/style/layers/fill_extrusion_layer.hpp", - "style/layers/fill_layer.hpp": "platform/android/src/style/layers/fill_layer.hpp", - "style/layers/heatmap_layer.hpp": "platform/android/src/style/layers/heatmap_layer.hpp", - "style/layers/hillshade_layer.hpp": "platform/android/src/style/layers/hillshade_layer.hpp", - "style/layers/layer.hpp": "platform/android/src/style/layers/layer.hpp", - "style/layers/layer_manager.hpp": "platform/android/src/style/layers/layer_manager.hpp", - "style/layers/line_layer.hpp": "platform/android/src/style/layers/line_layer.hpp", - "style/layers/raster_layer.hpp": "platform/android/src/style/layers/raster_layer.hpp", - "style/layers/symbol_layer.hpp": "platform/android/src/style/layers/symbol_layer.hpp", - "style/light.hpp": "platform/android/src/style/light.hpp", - "style/position.hpp": "platform/android/src/style/position.hpp", - "style/sources/custom_geometry_source.hpp": "platform/android/src/style/sources/custom_geometry_source.hpp", - "style/sources/geojson_source.hpp": "platform/android/src/style/sources/geojson_source.hpp", - "style/sources/image_source.hpp": "platform/android/src/style/sources/image_source.hpp", - "style/sources/raster_dem_source.hpp": "platform/android/src/style/sources/raster_dem_source.hpp", - "style/sources/raster_source.hpp": "platform/android/src/style/sources/raster_source.hpp", - "style/sources/source.hpp": "platform/android/src/style/sources/source.hpp", - "style/sources/unknown_source.hpp": "platform/android/src/style/sources/unknown_source.hpp", - "style/sources/vector_source.hpp": "platform/android/src/style/sources/vector_source.hpp", - "style/transition_options.hpp": "platform/android/src/style/transition_options.hpp", - "style/value.hpp": "platform/android/src/style/value.hpp", - "text/local_glyph_rasterizer_jni.hpp": "platform/android/src/text/local_glyph_rasterizer_jni.hpp" - } -} diff --git a/platform/android/filesource-files.json b/platform/android/filesource-files.json deleted file mode 100644 index ab92c2c32..000000000 --- a/platform/android/filesource-files.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "//": "This file can be edited manually and is the canonical source.", - "sources": [ - "platform/android/src/asset_manager_file_source.cpp", - "platform/android/src/connectivity_listener.cpp", - "platform/android/src/file_source.cpp", - "platform/android/src/http_file_source.cpp", - "platform/android/src/offline/offline_manager.cpp", - "platform/android/src/offline/offline_region.cpp", - "platform/android/src/offline/offline_region_definition.cpp", - "platform/android/src/offline/offline_region_error.cpp", - "platform/android/src/offline/offline_region_status.cpp", - "platform/default/src/mbgl/storage/sqlite3.cpp" - ], - "public_headers": {}, - "private_headers": { - "asset_manager.hpp": "platform/android/src/asset_manager.hpp", - "asset_manager_file_source.hpp": "platform/android/src/asset_manager_file_source.hpp", - "connectivity_listener.hpp": "platform/android/src/connectivity_listener.hpp", - "file_source.hpp": "platform/android/src/file_source.hpp", - "offline/offline_manager.hpp": "platform/android/src/offline/offline_manager.hpp", - "offline/offline_region.hpp": "platform/android/src/offline/offline_region.hpp", - "offline/offline_region_definition.hpp": "platform/android/src/offline/offline_region_definition.hpp", - "offline/offline_region_error.hpp": "platform/android/src/offline/offline_region_error.hpp", - "offline/offline_region_status.hpp": "platform/android/src/offline/offline_region_status.hpp" - } -} diff --git a/platform/android/gradle/android-nitpick.gradle b/platform/android/gradle/android-nitpick.gradle deleted file mode 100644 index 6db351967..000000000 --- a/platform/android/gradle/android-nitpick.gradle +++ /dev/null @@ -1,66 +0,0 @@ -apply from: "${rootDir}/gradle/dependencies.gradle" - -def MAPBOX_JAVA_DIR = 'mapbox-java' -def MAPBOX_JAVA_TAG_PREFIX = 'v' - -def MAPBOX_TELEMETRY_DIR = 'mapbox-events-android' -def MAPBOX_TELEMETRY_TAG_PREFIX = 'telem-' -def MAPBOX_CORE_TAG_PREFIX = '-core-' - -def MAPBOX_GESTURES_DIR = 'mapbox-gestures-android' -def MAPBOX_GESTURES_TAG_PREFIX = 'v' - -task androidNitpick { - doLast { - println "Running android nitpick script" - - println "Verify vendor submodule pins" - verifyVendorSubmodulePin(MAPBOX_JAVA_DIR, MAPBOX_JAVA_TAG_PREFIX, versions.mapboxServices) - verifyVendorSubmodulePin(MAPBOX_TELEMETRY_DIR, MAPBOX_TELEMETRY_TAG_PREFIX, - versions.mapboxTelemetry + MAPBOX_CORE_TAG_PREFIX + versions.mapboxCore) - verifyVendorSubmodulePin(MAPBOX_GESTURES_DIR, MAPBOX_GESTURES_TAG_PREFIX, versions.mapboxGestures) - - verifyLicenseGeneration() - } -} - -private def verifyVendorSubmodulePin(def dir, def prefix, def version) { - println "Verify vendor submodule pin: ${dir} (${prefix + version})" - exec { - workingDir = "${rootDir}/vendor/${dir}" - commandLine "git", "fetch", "-t" - } - - def output = new ByteArrayOutputStream() - exec { - workingDir = "${rootDir}/vendor/${dir}" - commandLine "git", "rev-list", "-n", "1", "tags/${prefix + version}" - standardOutput = output - } - def expectedCommit = output.toString().trim() - output.reset() - - exec { - workingDir = "${rootDir}/vendor/${dir}" - commandLine "git", "rev-parse", "HEAD" - standardOutput = output - } - def actualCommit = output.toString().trim() - - if (actualCommit != expectedCommit) { - throw new IllegalStateException("${dir} vendor repository is not checked out on the consumed binary's tag.\n" + - "Expected commit: " + expectedCommit + "(${prefix + version} tag).\n" + - "Actual commit: " + actualCommit + ".\n" + - "If you've updated the version in the dependencies.gradle file, make sure to bump the submodule pin in the platform/android/vendor/ directory to match the release tag.\n" + - "If you've bumped the pin, make sure to verify the version tag prefix in the android-nitpick.gradle file.") - } - output.close() -} - -private def verifyLicenseGeneration() { - println "Verify license generation with git diff..." - exec { - workingDir = "${rootDir}" - commandLine "python", "scripts/validate-license.py" - } -} \ No newline at end of file diff --git a/platform/android/gradle/artifact-settings.gradle b/platform/android/gradle/artifact-settings.gradle deleted file mode 100644 index acf29eeb8..000000000 --- a/platform/android/gradle/artifact-settings.gradle +++ /dev/null @@ -1,21 +0,0 @@ -ext { - mapboxArtifactGroupId = 'com.mapbox.mapboxsdk' - mapboxArtifactId = 'mapbox-android-sdk' - mapboxArtifactTitle = 'Mapbox Maps SDK for Android' - mapboxArtifactDescription = 'Mapbox Maps SDK for Android' - mapboxDeveloperName = 'Mapbox' - mapboxDeveloperId = 'mapbox' - mapboxArtifactUrl = 'https://github.com/mapbox/mapbox-gl-native' - mapboxArtifactVcsUrl = 'https://github.com/mapbox/mapbox-gl-native.git' - mapboxArtifactScmUrl = 'scm:git@github.com:mapbox/mapbox-gl-native.git' - mapboxArtifactLicenseName = 'BSD' - mapboxArtifactLicenseUrl = 'https://opensource.org/licenses/BSD-2-Clause' - versionName = project.hasProperty('VERSION_NAME') ? project.property('VERSION_NAME') : System.getenv('VERSION_NAME') - - mapboxBintrayUserOrg = 'mapbox' - mapboxBintrayRepoName = 'mapbox' - mapboxBintrayPackageName = 'com.mapbox.mapboxsdk:mapbox-android-sdk' - mapboxBintrayUser = project.hasProperty('BINTRAY_USER') ? project.property('BINTRAY_USER') : System.getenv('BINTRAY_USER') - mapboxBintrayApiKey = project.hasProperty('BINTRAY_API_KEY') ? project.property('BINTRAY_API_KEY') : System.getenv('BINTRAY_API_KEY') - mapboxGpgPassphrase = project.hasProperty('GPG_PASSPHRASE') ? project.property('GPG_PASSPHRASE') : System.getenv('GPG_PASSPHRASE') -} diff --git a/platform/android/gradle/dependencies.gradle b/platform/android/gradle/dependencies.gradle deleted file mode 100644 index 31633488e..000000000 --- a/platform/android/gradle/dependencies.gradle +++ /dev/null @@ -1,90 +0,0 @@ -ext { - - androidVersions = [ - minSdkVersion : 14, - targetSdkVersion : 28, - compileSdkVersion: 28, - ] - - versions = [ - mapboxServices : '4.9.0', - mapboxTelemetry : '4.5.1', - mapboxCore : '1.3.0', - mapboxGestures : '0.5.1', - mapboxAccounts : '0.2.0', - supportLib : '28.0.0', - constraintLayout: '1.1.2', - uiAutomator : '2.1.3', - espresso : '3.0.2', - testRunner : '1.0.2', - leakCanary : '1.6.3', - junit : '4.12', - mockito : '3.0.0', - mockk : '1.9.3', - robolectric : '4.3', - timber : '4.7.1', - okhttp : '3.12.3', - kotlin : '1.3.41', - licenses : '0.8.5', - bintray : '1.8.4', - artifactory : '4.9.3', - androidPublish : '3.6.2', - lint : '26.4.2', - gms : '16.0.0', - soLoader : '0.6.1', - jacoco : '0.8.4', - appcenter : '1.4', - ktlint : '0.34.0', - commonsIO : '2.6' - ] - - dependenciesList = [ - mapboxJavaGeoJSON : "com.mapbox.mapboxsdk:mapbox-sdk-geojson:${versions.mapboxServices}", - mapboxAndroidTelemetry : "com.mapbox.mapboxsdk:mapbox-android-telemetry:${versions.mapboxTelemetry}", - mapboxAndroidGestures : "com.mapbox.mapboxsdk:mapbox-android-gestures:${versions.mapboxGestures}", - mapboxAndroidAccounts : "com.mapbox.mapboxsdk:mapbox-android-accounts:${versions.mapboxAccounts}", - mapboxJavaTurf : "com.mapbox.mapboxsdk:mapbox-sdk-turf:${versions.mapboxServices}", - - junit : "junit:junit:${versions.junit}", - mockito : "org.mockito:mockito-core:${versions.mockito}", - mockk : "io.mockk:mockk:${versions.mockk}", - robolectric : "org.robolectric:robolectric:${versions.robolectric}", - - testRunner : "com.android.support.test:runner:${versions.testRunner}", - testRules : "com.android.support.test:rules:${versions.testRunner}", - testEspressoCore : "com.android.support.test.espresso:espresso-core:${versions.espresso}", - testEspressoIntents : "com.android.support.test.espresso:espresso-intents:${versions.espresso}", - testEspressoContrib : "com.android.support.test.espresso:espresso-contrib:${versions.espresso}", - testUiAutomator : "com.android.support.test.uiautomator:uiautomator-v18:${versions.uiAutomator}", - appCenter : "com.microsoft.appcenter:espresso-test-extension:${versions.appcenter}", - commonsIO : "commons-io:commons-io:${versions.commonsIO}", - supportAnnotations : "com.android.support:support-annotations:${versions.supportLib}", - supportAppcompatV7 : "com.android.support:appcompat-v7:${versions.supportLib}", - supportFragmentV4 : "com.android.support:support-fragment:${versions.supportLib}", - supportUtilV4 : "com.android.support:support-core-utils:${versions.supportLib}", - supportDesign : "com.android.support:design:${versions.supportLib}", - supportRecyclerView : "com.android.support:recyclerview-v7:${versions.supportLib}", - supportConstraintLayout: "com.android.support.constraint:constraint-layout:${versions.constraintLayout}", - - gmsLocation : "com.google.android.gms:play-services-location:${versions.gms}", - timber : "com.jakewharton.timber:timber:${versions.timber}", - okhttp3 : "com.squareup.okhttp3:okhttp:${versions.okhttp}", - leakCanaryDebug : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}", - leakCanaryRelease : "com.squareup.leakcanary:leakcanary-android-no-op:${versions.leakCanary}", - soLoader : "com.facebook.soloader:soloader:${versions.soLoader}", - - kotlinLib : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}", - kotlinPlugin : "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}", - licensesPlugin : "com.jaredsburrows:gradle-license-plugin:${versions.licenses}", - bintrayPlugin : "com.jfrog.bintray.gradle:gradle-bintray-plugin:${versions.bintray}", - artifactoryPlugin : "org.jfrog.buildinfo:build-info-extractor-gradle:${versions.artifactory}", - androidPublishPlugin : "digital.wup:android-maven-publish:${versions.androidPublish}", - jacocoPlugin : "org.jacoco:org.jacoco.core:${versions.jacoco}", - - lint : "com.android.tools.lint:lint:${versions.lint}", - lintApi : "com.android.tools.lint:lint-api:${versions.lint}", - lintChecks : "com.android.tools.lint:lint-checks:${versions.lint}", - lintTests : "com.android.tools.lint:lint-tests:${versions.lint}", - ktlint : "com.pinterest:ktlint:${versions.ktlint}" - ] -} diff --git a/platform/android/gradle/gradle-bintray.gradle b/platform/android/gradle/gradle-bintray.gradle deleted file mode 100644 index e8ace7359..000000000 --- a/platform/android/gradle/gradle-bintray.gradle +++ /dev/null @@ -1,117 +0,0 @@ -apply plugin: 'digital.wup.android-maven-publish' -apply plugin: 'maven-publish' -apply plugin: 'com.jfrog.bintray' -apply plugin: 'com.jfrog.artifactory' -apply from: file('../gradle/artifact-settings.gradle') - -version = project.ext.versionName -group = project.ext.mapboxArtifactGroupId - -publishing { - publications { - MapboxMapsSdkPublication(MavenPublication) { - from components.android - groupId this.group - artifactId project.ext.mapboxArtifactId - version this.version - - afterEvaluate { - artifact(androidSourcesJar) - artifact(androidJavadocsJar) - } - - pom.withXml { - final mainNode = asNode() - mainNode.appendNode('name', project.ext.mapboxArtifactTitle) - mainNode.appendNode('description', project.ext.mapboxArtifactTitle) - mainNode.appendNode('url', project.ext.mapboxArtifactUrl) - - final licenseNode = mainNode.appendNode('licenses').appendNode('license') - licenseNode.appendNode('name', project.ext.mapboxArtifactLicenseName) - licenseNode.appendNode('url', project.ext.mapboxArtifactLicenseUrl) - licenseNode.appendNode('distribution', "repo") - - final developerNode = mainNode.appendNode('developers').appendNode('developer') - developerNode.appendNode('id', project.ext.mapboxDeveloperId) - developerNode.appendNode('name', project.ext.mapboxDeveloperName) - - final scmNode = mainNode.appendNode("scm") - scmNode.appendNode("connection", project.ext.mapboxArtifactScmUrl) - scmNode.appendNode("developerConnection", project.ext.mapboxArtifactScmUrl) - scmNode.appendNode("url", project.ext.mapboxArtifactUrl) - } - } - } -} - -bintray { - user = mapboxBintrayUser - key = mapboxBintrayApiKey - publications('MapboxMapsSdkPublication') - pkg { - repo = project.ext.mapboxBintrayRepoName - name = project.ext.mapboxBintrayPackageName - userOrg = project.ext.mapboxBintrayUserOrg - licenses = [project.ext.mapboxArtifactLicenseName] - vcsUrl = project.ext.mapboxArtifactVcsUrl - publish = false - version { - name = project.ext.versionName - desc = project.ext.mapboxArtifactDescription - released = new Date() - gpg { - sign = true - passphrase = mapboxGpgPassphrase - } - mavenCentralSync { - sync = false - } - } - } -} - -artifactory { - contextUrl = 'http://oss.jfrog.org' - publish { - repository { - repoKey = 'oss-snapshot-local' - username = mapboxBintrayUser - password = mapboxBintrayApiKey - } - defaults { - publications('MapboxMapsSdkPublication') - } - } -} - -task androidJavadocs(type: Javadoc) { - source = android.sourceSets.main.java.sourceFiles - classpath = files(android.bootClasspath) - failOnError = false -} - -task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - classifier = 'javadoc' - from androidJavadocs.destinationDir -} - -task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.sourceFiles -} - -tasks.withType(Javadoc) { - options.addStringOption('encoding', 'UTF-8') - options.addStringOption('docencoding', 'UTF-8') - options.addStringOption('charset', 'UTF-8') -} - -afterEvaluate { project -> - android.libraryVariants.all { variant -> - tasks.androidJavadocs.doFirst { - classpath += files(variant.javaCompile.classpath.files) - } - } -} - - diff --git a/platform/android/gradle/gradle-checkstyle.gradle b/platform/android/gradle/gradle-checkstyle.gradle deleted file mode 100644 index 8eb4baa85..000000000 --- a/platform/android/gradle/gradle-checkstyle.gradle +++ /dev/null @@ -1,26 +0,0 @@ -apply plugin: 'checkstyle' - -checkstyle { - toolVersion = "7.1.1" // 7.3 - configFile = "../checkstyle.xml" as File -} - -task checkstyle(type: Checkstyle) { - description 'Checks if the code adheres to coding standards' - group 'verification' - configFile file("../checkstyle.xml") - source 'src' - include '**/*.java' - exclude '**/gen/**' - exclude '**/style/*LayerTest.java' - exclude '**/style/LightTest.java' - exclude '**/style/layers/Property.java' - exclude '**/style/layers/PropertyFactory.java' - exclude '**/style/layers/*Layer.java' - exclude '**/style/light/Light.java' - exclude '**/log/LoggerDefinition.java' - exclude '**/log/Logger.java' - exclude '**/Expression.java' - classpath = files() - ignoreFailures = false -} diff --git a/platform/android/gradle/gradle-config.gradle b/platform/android/gradle/gradle-config.gradle deleted file mode 100644 index 834680663..000000000 --- a/platform/android/gradle/gradle-config.gradle +++ /dev/null @@ -1,23 +0,0 @@ -// -// Configuration file for gradle build execution. -// - -task accessToken { - def tokenFile = new File("${projectDir}/src/main/res/values/developer-config.xml") - if (!tokenFile.exists()) { - String mapboxAccessToken = "$System.env.MAPBOX_ACCESS_TOKEN" - if (mapboxAccessToken == "null") { - System.out.println("You should set the MAPBOX_ACCESS_TOKEN environment variable.") - mapboxAccessToken = "YOUR_MAPBOX_ACCESS_TOKEN_GOES_HERE" - } - String tokenFileContents = "\n" + - "\n" + - " " + mapboxAccessToken + "\n" + - "" - tokenFile.write(tokenFileContents) - } -} - -gradle.projectsEvaluated { - preBuild.dependsOn('accessToken') -} diff --git a/platform/android/gradle/gradle-dependencies-graph.gradle b/platform/android/gradle/gradle-dependencies-graph.gradle deleted file mode 100644 index 5cbc7b974..000000000 --- a/platform/android/gradle/gradle-dependencies-graph.gradle +++ /dev/null @@ -1,29 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } - - dependencies { - classpath "com.vanniktech:gradle-dependency-graph-generator-plugin:0.3.0" - } -} - -import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorPlugin -import com.vanniktech.dependency.graph.generator.DependencyGraphGeneratorExtension.Generator -import com.vanniktech.dependency.graph.generator.dot.GraphFormattingOptions -import com.vanniktech.dependency.graph.generator.dot.Color -import com.vanniktech.dependency.graph.generator.dot.Shape -import com.vanniktech.dependency.graph.generator.dot.Style - -plugins.apply(DependencyGraphGeneratorPlugin) - -def mapboxGenerator = new Generator( - "mapboxLibraries", // Suffix for our Gradle task. - "", // Root suffix that we don't want in this case. - { dependency -> dependency.getModuleGroup().startsWith("com.mapbox.mapboxsdk") }, // Only want Mapbox libs. - { dependency -> true }, // Include transitive dependencies. -) - -dependencyGraphGenerator { - generators = [mapboxGenerator] -} diff --git a/platform/android/gradle/gradle-javadoc.gradle b/platform/android/gradle/gradle-javadoc.gradle deleted file mode 100644 index ba530a9b6..000000000 --- a/platform/android/gradle/gradle-javadoc.gradle +++ /dev/null @@ -1,18 +0,0 @@ -android.libraryVariants.all { variant -> - def name = variant.name - task "javadoc$name"(type: Javadoc) { - description = "Generates javadoc for build $name" - destinationDir = new File(destinationDir, variant.baseName) - source = variant.sourceSets.collect { it.java.sourceFiles }.inject { m, i -> m + i } - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - options.windowTitle("Mapbox Maps SDK for Android $VERSION_NAME Reference") - options.docTitle("Mapbox Maps SDK for Android $VERSION_NAME") - options.header("Mapbox Maps SDK for Android $VERSION_NAME Reference") - options.bottom("© 2015–2019 Mapbox. All rights reserved.") - options.links("http://docs.oracle.com/javase/7/docs/api/") - options.linksOffline "http://d.android.com/reference","${android.sdkDirectory}/docs/reference" - options.overview("src/main/java/overview.html") - options.group("Mapbox Android SDK", "com.mapbox.*") - exclude '**/R.java', '**/BuildConfig.java', '**/**.java.ejs', '**/**.html' - } -} \ No newline at end of file diff --git a/platform/android/gradle/gradle-lint.gradle b/platform/android/gradle/gradle-lint.gradle deleted file mode 100644 index cbebeaa74..000000000 --- a/platform/android/gradle/gradle-lint.gradle +++ /dev/null @@ -1,30 +0,0 @@ -task ciLint(type: Copy) { - if (isLocalBuild()) { - from "${projectDir}/lint/lint-baseline-local.xml" - into "${projectDir}" - rename { String fileName -> - fileName.replace("lint-baseline-local.xml", "lint-baseline.xml") - } - } else { - from "${projectDir}/lint/lint-baseline-ci.xml" - into "${projectDir}" - rename { String fileName -> - fileName.replace("lint-baseline-ci.xml", "lint-baseline.xml") - } - } -} - -def isLocalBuild() { - if (System.getenv('IS_LOCAL_DEVELOPMENT') != null) { - return System.getenv('IS_LOCAL_DEVELOPMENT').toBoolean() - } - return true -} - -lint.dependsOn ciLint - -tasks.whenTaskAdded { task -> - if (task.name == 'lintVitalRelease') { - task.dependsOn ciLint - } -} \ No newline at end of file diff --git a/platform/android/gradle/gradle-make.gradle b/platform/android/gradle/gradle-make.gradle deleted file mode 100644 index 65d971cc9..000000000 --- a/platform/android/gradle/gradle-make.gradle +++ /dev/null @@ -1,18 +0,0 @@ -// -// Contains GL-native make commands -// - -task makeClean(type: Exec) { - workingDir '../../' - commandLine 'make', 'clean' -} - -task makeAndroid(type: Exec) { - workingDir '../../' - commandLine 'make', 'android' -} - -task makeAndroidAll(type: Exec) { - workingDir '../../' - commandLine 'make', 'apackage' -} diff --git a/platform/android/gradle/gradle-update-vendor-modules.gradle b/platform/android/gradle/gradle-update-vendor-modules.gradle deleted file mode 100644 index e215450ec..000000000 --- a/platform/android/gradle/gradle-update-vendor-modules.gradle +++ /dev/null @@ -1,8 +0,0 @@ -task updateVendorSubmodules { - doLast { - exec { - workingDir = "${rootDir}" - commandLine "git", "submodule", "update", "--init", "vendor" - } - } -} \ No newline at end of file diff --git a/platform/android/gradle/jacoco-report.gradle b/platform/android/gradle/jacoco-report.gradle deleted file mode 100644 index 64c99e09f..000000000 --- a/platform/android/gradle/jacoco-report.gradle +++ /dev/null @@ -1,33 +0,0 @@ -apply plugin: 'jacoco' -apply from: "${rootDir}/gradle/dependencies.gradle" - -jacoco { - toolVersion = versions.jacoco -} - -task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest']) { - group = "Reporting" - description = "Combine code coverage to unified report." - - reports { - xml.enabled = true - html.enabled = true - } - - def fileExcludes = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*'] - def debugTree = fileTree(dir: "${project.buildDir}/intermediates/javac/debug/classes", excludes: fileExcludes) - def mainSrc = "${project.projectDir}/src/main/java" - println(mainSrc) - def ecSrc = fileTree(dir: "$project.buildDir", include: "**/*.ec") - def execSrc = fileTree(dir: "$project.buildDir", include: "**/*.exec") - - doFirst { - def files = files([ecSrc, execSrc]).files - println "Creating Jacoco Report for ${files.size()} coverage files" - files.each { file -> println file } - } - - sourceDirectories.from = files([mainSrc]) - classDirectories.from = files([debugTree]) - executionData.from = files([ecSrc, execSrc]) -} \ No newline at end of file diff --git a/platform/android/gradle/ktlint.gradle b/platform/android/gradle/ktlint.gradle deleted file mode 100644 index 31743ccd4..000000000 --- a/platform/android/gradle/ktlint.gradle +++ /dev/null @@ -1,27 +0,0 @@ -apply from: "${rootDir}/gradle/dependencies.gradle" - -repositories { - jcenter() -} - -configurations { - ktlint -} - -dependencies { - ktlint dependenciesList.ktlint -} - -task ktlint(type: JavaExec, group: "verification") { - description = "Check Kotlin code style." - classpath = configurations.ktlint - main = "com.pinterest.ktlint.Main" - args "src/**/*.kt" -} - -task ktlintFormat(type: JavaExec, group: "formatting") { - description = "Fix Kotlin code style deviations." - classpath = configurations.ktlint - main = "com.pinterest.ktlint.Main" - args "-F", "src/**/*.kt" -} \ No newline at end of file diff --git a/platform/android/gradle/native-build.gradle b/platform/android/gradle/native-build.gradle deleted file mode 100644 index 0c16c379d..000000000 --- a/platform/android/gradle/native-build.gradle +++ /dev/null @@ -1,70 +0,0 @@ -ext.nativeBuild = { nativeTargets -> - android { - defaultPublishConfig project.hasProperty("mapbox.buildtype") ? project.getProperty("mapbox.buildtype") : "debug" - - // We sometimes want to invoke Gradle without building a native dependency, e.g. when we just want - // to invoke the Java tests. When we explicitly specify an ABI of 'none', no native dependencies are - // added. When another ABI is specified explicitly, we're just going to build that ABI. In all other - // cases, all ABIs are built. - // - // When invoking from the command line or to override the device default, set `-Pmapbox.abis=...` to - // only build the desired architectures. - // - // When building from Android Studio, gradle.properties sets `android.buildOnlyTargetAbi=true` so that - // only the architecture for the device you're running on gets built. - def abi = 'all' - if (!project.hasProperty('android.injected.invoked.from.ide') || project.hasProperty("mapbox.abis")) { - // Errors when the user invokes Gradle from the command line and didn't set mapbox.abis - abi = project.getProperty("mapbox.abis") - } - - if (abi != 'none') { - externalNativeBuild { - cmake { - path "../../../CMakeLists.txt" - version "3.10.2" - } - } - } - - // Allow determining the C++ STL we're using when building Mapbox GL. - def stl = 'c++_static' - if (project.hasProperty("mapbox.stl")) { - stl = project.getProperty("mapbox.stl") - } - - defaultConfig { - if (abi != 'none') { - externalNativeBuild { - cmake { - arguments "-DANDROID_TOOLCHAIN=clang" - arguments "-DANDROID_STL=" + stl - arguments "-DANDROID_CPP_FEATURES=exceptions" - arguments "-DMBGL_PLATFORM=android" - arguments "-DMASON_PLATFORM=android" - arguments "-DNodeJS_EXECUTABLE=" + node - arguments "-Dnpm_EXECUTABLE=" + npm - - // Enable ccache if the user has installed it. - if (ccache?.trim()) { - arguments "-DANDROID_CCACHE=" + ccache - } - - cFlags "-Qunused-arguments" - cppFlags "-Qunused-arguments" - - for (target in nativeTargets) { - targets target - } - - if (abi != 'all') { - abiFilters abi.split(' ') - } else { - abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64" - } - } - } - } - } - } -} \ No newline at end of file diff --git a/platform/android/gradle/wrapper/gradle-wrapper.jar b/platform/android/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 51288f9c2f05faf8d42e1a751a387ca7923882c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 54227 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 iyjj \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Escape application args -save ( ) { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=$(save "$@") - -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" - -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - -exec "$JAVACMD" "$@" diff --git a/platform/android/gradlew.bat b/platform/android/gradlew.bat deleted file mode 100644 index e95643d6a..000000000 --- a/platform/android/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/platform/android/resources/compass.svg b/platform/android/resources/compass.svg deleted file mode 100755 index fd27e73f2..000000000 --- a/platform/android/resources/compass.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/platform/android/resources/direction-dot.svg b/platform/android/resources/direction-dot.svg deleted file mode 100755 index 14ce0bf7f..000000000 --- a/platform/android/resources/direction-dot.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/platform/android/resources/dot-arrow.svg b/platform/android/resources/dot-arrow.svg deleted file mode 100644 index 4c2841a54..000000000 --- a/platform/android/resources/dot-arrow.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - diff --git a/platform/android/resources/location-dot.svg b/platform/android/resources/location-dot.svg deleted file mode 100755 index 24addea3c..000000000 --- a/platform/android/resources/location-dot.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/platform/android/resources/location-static-dot.svg b/platform/android/resources/location-static-dot.svg deleted file mode 100755 index 18269bcdf..000000000 --- a/platform/android/resources/location-static-dot.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/platform/android/scripts/exclude-activity-gen.json b/platform/android/scripts/exclude-activity-gen.json deleted file mode 100644 index ab84957e3..000000000 --- a/platform/android/scripts/exclude-activity-gen.json +++ /dev/null @@ -1,53 +0,0 @@ -[ - "BaseLocationActivity", - "MapSnapshotterMarkerActivity", - "MapSnapshotterReuseActivity", - "MapSnapshotterLocalStyleActivity", - "LatLngBoundsActivity", - "BottomSheetActivity", - "MockLocationEngine", - "DeleteRegionActivity", - "RealTimeGeoJsonActivity", - "UpdateMetadataActivity", - "CarDrivingActivity", - "MyLocationTrackingModeActivity", - "MyLocationToggleActivity", - "MyLocationTintActivity", - "MyLocationDrawableActivity", - "DoubleMapActivity", - "LocationPickerActivity", - "GeoJsonClusteringActivity", - "RuntimeStyleTestActivity", - "AnimatedSymbolLayerActivity", - "ViewPagerActivity", - "MapFragmentActivity", - "SupportMapFragmentActivity", - "SnapshotActivity", - "NavigationDrawerActivity", - "QueryRenderedFeaturesBoxHighlightActivity", - "MultiMapActivity", - "MapInDialogActivity", - "ManualZoomActivity", - "MaxMinZoomActivity", - "ScrollByActivity", - "ZoomFunctionSymbolLayerActivity", - "SymbolGeneratorActivity", - "TextureViewTransparentBackgroundActivity", - "SimpleMapActivity", - "RenderTestActivity", - "SymbolLayerActivity", - "LocationFragmentActivity", - "MergeOfflineRegionsActivity", - "NestedViewPagerActivity", - "DebugModeActivity", - "TextureViewDebugModeActivity", - "RuntimeStyleActivity", - "GridSourceActivity", - "AnimatedImageSourceActivity", - "EspressoTestActivity", - "ChangeResourcesCachePathActivity", - "EspressoTestActivity", - "FragmentBackStackActivity", - "ChildFragmentMapInDialogActivity", - "PerformanceMeasurementActivity" -] diff --git a/platform/android/scripts/generate-license.py b/platform/android/scripts/generate-license.py deleted file mode 100644 index 9e59cb418..000000000 --- a/platform/android/scripts/generate-license.py +++ /dev/null @@ -1,39 +0,0 @@ -#!/usr/bin/python - -import os -import json - -path = os.getcwd() + "/platform/android/" -with open(path + "LICENSE.md", 'w') as licenseFile: - licenseFile.write(" \n") - licenseFile.write("## Additional Mapbox GL licenses \n") - with open(path + "MapboxGLAndroidSDK/build/reports/licenses/licenseReleaseReport.json", 'r') as dataFile: - data = json.load(dataFile) - - gradleLicensePlugin =""" - { - "project": "Gradle License Plugin", - "url": "https://github.com/jaredsburrows/gradle-license-plugin", - "licenses": [ - { - "license": "The Apache Software License, Version 2.0", - "license_url": "http://www.apache.org/licenses/LICENSE-2.0.txt" - } - ] - } - """ - data.append(json.loads(gradleLicensePlugin)) - - licenseName = "" - licenseUrl = "" - for entry in data: - projectName = entry["project"] - projectUrl = entry["url"] - for license in entry["licenses"]: - licenseName = license["license"] - licenseUrl = license["license_url"] - - licenseFile.write("Mapbox GL uses portions of the %s. \n" % projectName + - ("URL: [%s](%s) \n" % (projectUrl, projectUrl) if projectUrl is not None else "") + - "License: [%s](%s)" % (licenseName, licenseUrl) + - "\n\n===========================================================================\n\n") diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js deleted file mode 100755 index 3265c161e..000000000 --- a/platform/android/scripts/generate-style-code.js +++ /dev/null @@ -1,400 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -const fs = require('fs'); -const ejs = require('ejs'); -const spec = require('../../../scripts/style-spec'); -const _ = require('lodash'); - -// FIXME: https://github.com/mapbox/mapbox-gl-native/issues/15008 -delete spec.layout_circle["circle-sort-key"] -delete spec.layout_line["line-sort-key"] -delete spec.layout_fill["fill-sort-key"] - -require('../../../scripts/style-code'); - -// Specification parsing // -const lightProperties = Object.keys(spec[`light`]).reduce((memo, name) => { - var property = spec[`light`][name]; - property.name = name; - property['light-property'] = true; - property.doc = property.doc.replace(/°/g,'°'); - memo.push(property); - return memo; -}, []); - -// Collect layer types from spec -var layers = Object.keys(spec.layer.type.values).map((type) => { - const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => { - if (name !== 'visibility') { - spec[`layout_${type}`][name].name = name; - memo.push(spec[`layout_${type}`][name]); - } - return memo; - }, []); - - const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => { - spec[`paint_${type}`][name].name = name; - memo.push(spec[`paint_${type}`][name]); - return memo; - }, []); - - return { - type: type, - doc: spec.layer.type.values[type].doc, - layoutProperties: layoutProperties, - paintProperties: paintProperties, - properties: layoutProperties.concat(paintProperties) - }; -}); - -// Process all layer properties -const uniqueArrayEnum = (prop, enums) => { - if (prop.value !== 'enum') return false; - const enumsEqual = (val1, val2) => val1.length === val1.length && val1.every((val, i) => val === val2[i]); - return enums.filter(e => enumsEqual(Object.keys(prop.values).sort(), Object.keys(e.values).sort())).length == 0; -}; - -const layoutProperties = _(layers).map('layoutProperties').flatten().value(); -const paintProperties = _(layers).map('paintProperties').flatten().value(); -const allProperties = _(layoutProperties).union(paintProperties).union(lightProperties).value(); -let allEnumProperties = _(allProperties).filter({'type': 'enum'}).value(); -const uniqueArrayEnumProperties = _(allProperties).filter({'type': 'array'}).filter(prop => uniqueArrayEnum(prop, allEnumProperties)).value(); -const enumProperties = _(allEnumProperties).union(uniqueArrayEnumProperties).value(); - - -global.propertyType = function propertyType(property) { - switch (property.type) { - case 'boolean': - return 'Boolean'; - case 'number': - return 'Float'; - case 'formatted': - return 'Formatted'; - case 'string': - case 'resolvedImage': - return 'String'; - case 'enum': - return 'String'; - case 'color': - return 'String'; - case 'array': - return `${propertyType({type:property.value})}[]`; - default: - throw new Error(`unknown type for ${property.name}`); - } -} - -global.propertyJavaType = function propertyType(property) { - switch (property.type) { - case 'boolean': - return 'boolean'; - case 'number': - return 'float'; - case 'formatted': - return 'Formatted'; - case 'string': - case 'resolvedImage': - return 'String'; - case 'enum': - return 'String'; - case 'color': - return 'String'; - case 'array': - return `${propertyJavaType({type:property.value})}[]`; - default: - throw new Error(`unknown type for ${property.name}`); - } - } - -global.propertyJNIType = function propertyJNIType(property) { - switch (property.type) { - case 'boolean': - return 'jboolean'; - case 'number': - return 'jfloat'; - case 'String': - return 'String'; - case 'enum': - return 'String'; - case 'color': - return 'String'; - case 'array': - return `jarray<${propertyType({type:property.value})}[]>`; - default: - return 'jobject*'; - } -} - -global.propertyNativeType = function (property) { - if (/-translate-anchor$/.test(property.name)) { - return 'TranslateAnchorType'; - } - if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { - return 'AlignmentType'; - } - if (/^(text|icon)-anchor$/.test(property.name)) { - return 'SymbolAnchorType'; - } - switch (property.type) { - case 'boolean': - return 'bool'; - case 'number': - return 'float'; - case 'formatted': - case 'string': - case 'resolvedImage': // TODO: replace once we implement image expressions - return 'std::string'; - case 'enum': - if(property['light-property']){ - return `Light${camelize(property.name)}Type`; - } - return `${camelize(property.name)}Type`; - case 'color': - return `Color`; - case 'array': - if (property.length) { - return `std::array<${propertyType({type: property.value})}, ${property.length}>`; - } else { - return `std::vector<${propertyType({type: property.value})}>`; - } - default: throw new Error(`unknown type for ${property.name}`) - } -} - -global.propertyTypeAnnotation = function propertyTypeAnnotation(property) { - switch (property.type) { - case 'enum': - return `@Property.${snakeCaseUpper(property.name)}`; - default: - return ""; - } -}; - -global.defaultExpressionJava = function(property) { - switch (property.type) { - case 'boolean': - return 'boolean'; - case 'number': - return 'number'; - case 'formatted': - return 'format'; - case 'resolvedImage': - return "image"; - case 'string': - case 'enum': - return "string"; - case 'color': - return 'toColor'; - case 'array': - return "array"; - default: return "string"; - } -} - -global.defaultValueJava = function(property) { - if(property.name.endsWith("-pattern")) { - return '"pedestrian-polygon"'; - } - if(property.name.endsWith("-font")) { - return 'new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}'; - } - switch (property.type) { - case 'boolean': - return 'true'; - case 'number': - return '0.3f'; - case 'formatted': - return 'new Formatted(new FormattedSection("default"))' - case 'string': - case 'resolvedImage': - return '"' + property['default'] + '"'; - case 'enum': - return snakeCaseUpper(property.name) + "_" + snakeCaseUpper(Object.keys(property.values)[0]); - case 'color': - return '"rgba(255,128,0,0.7)"'; - case 'array': - switch (property.value) { - case 'string': - case 'enum': - if (property['default'] !== undefined) { - return '[' + property['default'] + ']'; - } else { - return 'new String[0]'; - } - case 'number': - var result ='new Float[] {'; - for (var i = 0; i < property.length; i++) { - result += "0f"; - if (i +1 != property.length) { - result += ", "; - } - } - return result + "}"; - } - default: throw new Error(`unknown type for ${property.name}`) - } -} - -/** - * Produces documentation for property factory methods - */ -global.propertyFactoryMethodDoc = function (property) { - var replaceIfPixels = function (doc) { - return doc.replace('pixels', 'density-independent pixels') - } - let doc = replaceIfPixels(property.doc); - // Match other items in back ticks - doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) { - if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0] && _(enumProperties).filter({'name': symbol}).value().length > 0) { - // Property 'enums' - symbol = snakeCaseUpper(symbol); - return '{@link Property.' + symbol + '}'; - } else if( _(allProperties).filter({'name': symbol}).value().length > 0) { - // Other properties - return '{@link PropertyFactory#' + camelizeWithLeadingLowercase(symbol) + '}'; - } else { - // Left overs - return '`' + symbol + '`'; - } - }); - return doc; -}; - -/** - * Produces documentation for property value constants - */ -global.propertyValueDoc = function (property, value) { - - // Match references to other property names & values. - // Requires the format 'When `foo` is set to `bar`,'. - let doc = property.values[value].doc.replace(/When `(.+?)` is set to `(.+?)`(?: or `([^`]+?)`)?,/g, function (m, peerPropertyName, propertyValue, secondPropertyValue, offset, str) { - let otherProperty = snakeCaseUpper(peerPropertyName); - let otherValue = snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(propertyValue); - const firstPropertyValue = 'When {@link ' + `${otherProperty}` + '} is set to {@link Property#' + `${otherValue}` + '}'; - if (secondPropertyValue) { - return firstPropertyValue + ` or {@link Property#${snakeCaseUpper(peerPropertyName) + '_' + snakeCaseUpper(secondPropertyValue)}},`; - } else { - return firstPropertyValue + ','; - } - }); - - // Match references to our own property values. - // Requires the format 'is equivalent to `bar`'. - doc = doc.replace(/is equivalent to `(.+?)`/g, function(m, propertyValue, offset, str) { - propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(propertyValue); - return 'is equivalent to {@link Property#' + propertyValue + '}'; - }); - - // Match other items in back ticks - doc = doc.replace(/`(.+?)`/g, function (m, symbol, offset, str) { - if ('values' in property && Object.keys(property.values).indexOf(symbol) !== -1) { - // Property values - propertyValue = snakeCaseUpper(property.name) + '_' + snakeCaseUpper(symbol); - console.log("Transforming", symbol, propertyValue); - return '{@link Property#' + `${propertyValue}` + '}'; - } else if (str.substr(offset - 4, 3) !== 'CSS' && symbol[0].toUpperCase() != symbol[0]) { - // Property 'enums' - if (symbol === 'symbol-sort-key') { - return 'symbol sort key'; - } - - symbol = snakeCaseUpper(symbol); - return '{@link ' + symbol + '}'; - } else { - // Left overs - return symbol - } - }); - return doc; -}; - -global.isLightProperty = function (property) { - return property['light-property'] === true; -}; - -global.propertyValueType = function (property) { - switch (property['property-type']) { - default: - return `PropertyValue<${evaluatedType(property)}>`; - } -}; - -global.evaluatedType = function (property) { - if (/-translate-anchor$/.test(property.name)) { - return 'TranslateAnchorType'; - } - if (/-(rotation|pitch|illumination)-alignment$/.test(property.name)) { - return 'AlignmentType'; - } - if (/^(text|icon)-anchor$/.test(property.name)) { - return 'SymbolAnchorType'; - } - if (/position/.test(property.name)) { - return 'Position'; - } - switch (property.type) { - case 'boolean': - return 'bool'; - case 'number': - return 'float'; - case 'formatted': - case 'string': - case 'image': - return 'std::string'; - case 'enum': - return (isLightProperty(property) ? 'Light' : '') + `${camelize(property.name)}Type`; - case 'color': - return `Color`; - case 'array': - if (property.length) { - return `std::array<${evaluatedType({type: property.value})}, ${property.length}>`; - } else { - return `std::vector<${evaluatedType({type: property.value})}>`; - } - default: throw new Error(`unknown type for ${property.name}`) - } -}; - -global.supportsZoomFunction = function (property) { - return property.expression && property.expression.parameters.indexOf('zoom') > -1; -}; - -global.supportsPropertyFunction = function (property) { - return property['property-type'] === 'data-driven' || property['property-type'] === 'cross-faded-data-driven'; -}; - -// Template processing // - -// Java + JNI Light (Peer model) -const lightJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/light.java.ejs', 'utf8'), {strict: true}); -const lightJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/light.junit.ejs', 'utf8'), {strict: true}); -writeIfModified(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java`, lightJava({properties: lightProperties})); -writeIfModified(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LightTest.java`, lightJavaUnitTests({properties: lightProperties})); - -// Java + JNI Layers (Peer model) -const layerHpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.hpp.ejs', 'utf8'), {strict: true}); -const layerCpp = ejs.compile(fs.readFileSync('platform/android/src/style/layers/layer.cpp.ejs', 'utf8'), {strict: true}); -const layerJava = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/layer.java.ejs', 'utf8'), {strict: true}); -const layerJavaUnitTests = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/layer.junit.ejs', 'utf8'), {strict: true}); - -for (const layer of layers) { - writeIfModified(`platform/android/src/style/layers/${layer.type.replace('-', '_')}_layer.hpp`, layerHpp(layer)); - writeIfModified(`platform/android/src/style/layers/${layer.type.replace('-', '_')}_layer.cpp`, layerCpp(layer)); - writeIfModified(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/${camelize(layer.type)}Layer.java`, layerJava(layer)); - writeIfModified(`platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/${camelize(layer.type)}LayerTest.java`, layerJavaUnitTests(layer)); -} - -// Java PropertyFactory -const propertiesTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property_factory.java.ejs', 'utf8'), {strict: true}); -writeIfModified( - `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java`, - propertiesTemplate({layoutProperties: layoutProperties, paintProperties: paintProperties}) -); - -// Java Property -const enumPropertyJavaTemplate = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/property.java.ejs', 'utf8'), {strict: true}); -writeIfModified( - `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java`, - enumPropertyJavaTemplate({properties: enumProperties}) -); diff --git a/platform/android/scripts/generate-test-code.js b/platform/android/scripts/generate-test-code.js deleted file mode 100644 index 64b38e5ac..000000000 --- a/platform/android/scripts/generate-test-code.js +++ /dev/null @@ -1,73 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const ejs = require('ejs'); - -global.iff = function (condition, val) { - return condition() ? val : ""; -} - -global.camelize = function (str) { - return str.replace(/(?:^|-)(.)/g, function (_, x) { - return x.toUpperCase(); - }); -} - -const excludeClasses = JSON.parse(fs.readFileSync('platform/android/scripts/exclude-activity-gen.json', 'utf8')); -const appBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity'; -const testBasePath = 'platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/gen'; -const subPackages = fs.readdirSync(appBasePath); -const ejsConversionTask = ejs.compile(fs.readFileSync('platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/activity/activity.junit.ejs', 'utf8'), {strict: true}); - -if (!fs.existsSync(testBasePath)){ - fs.mkdirSync(testBasePath); -} - -console.log("\nGenerating test activities:\n"); -var generatedClasses = []; -var excludedClasses = []; -for(const subPackage of subPackages) { - if(!(subPackage.slice(-5) == '.java')) { - const activities = fs.readdirSync(appBasePath+'/'+subPackage); - - // create directories for package - if (!fs.existsSync(testBasePath+"/"+subPackage)){ - fs.mkdirSync(testBasePath+"/"+subPackage); - } - - for (const activity of activities) { - var activityName; - if (activity.slice(-5) === '.java') { - // .java file - activityName = activity.slice(0, -5); - } else { - // .kt file - activityName = activity.slice(0, -3); - } - - // create path for test file - const filePath = testBasePath+"/"+subPackage+"/"+activityName+'Test.java'; - - // try removing previous generated files - try { - fs.accessSync(filePath, fs.F_OK); - fs.unlinkSync(filePath); - } catch (e) { - } - - // only generate test file if not part of exclude list + if contains Activity in name - if ((!(excludeClasses.indexOf(activityName) > -1)) && activityName.includes("Activity")) { - fs.writeFileSync(filePath, ejsConversionTask([activityName, subPackage])); - generatedClasses.push(activityName); - }else{ - excludedClasses.push(activityName); - } - } - } -} - -for(const generatedClass of generatedClasses){ - console.log(generatedClass+"Test"); -} - -console.log("\nFinished generating " + generatedClasses.length + " activity sanity tests, excluded " + excludeClasses.length + " classes.\n"); \ No newline at end of file diff --git a/platform/android/scripts/metrics.sh b/platform/android/scripts/metrics.sh deleted file mode 100755 index cc33daf17..000000000 --- a/platform/android/scripts/metrics.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -o pipefail - -# Track individual architectures -scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/armeabi-v7a/libmapbox-gl.so" "Android arm-v7" -scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/arm64-v8a/libmapbox-gl.so" "Android arm-v8" -scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/x86/libmapbox-gl.so" "Android x86" -scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/intermediates/intermediate-jars/release/jni/x86_64/libmapbox-gl.so" "Android x86_64" - -# Track overall library size -scripts/check_binary_size.js "platform/android/MapboxGLAndroidSDK/build/outputs/aar/MapboxGLAndroidSDK-release.aar" "Android AAR" - -if [[ $CIRCLE_BRANCH == master ]]; then - # Build source data for http://mapbox.github.io/mapbox-gl-native/metrics/binary-size/ - # and log binary sizes to metrics warehouse - scripts/publish_binary_size.js -fi diff --git a/platform/android/scripts/parse-jacoco-report.py b/platform/android/scripts/parse-jacoco-report.py deleted file mode 100755 index 4d06fda6c..000000000 --- a/platform/android/scripts/parse-jacoco-report.py +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/python - -import os -import re -from io import open - -reportPath = os.getcwd() + "/platform/android/MapboxGLAndroidSDK/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml" -with open(reportPath, 'r', encoding='utf-8') as jacocoReport: - line = jacocoReport.readline().strip() - - # find the last INSTRUCTION coverage report which is a sum of all separate ones - instructionIndex = line.rfind('type="INSTRUCTION"') - startIndex = line.find('missed', instructionIndex) - endIndex = line.find('/>', startIndex) - - # find the missed and covered lines counts - numbers = re.match(r'missed="(\d+)" covered="(\d+)"', line[startIndex:endIndex]) - missed = int(numbers.group(1)) - covered = int(numbers.group(2)) - - # calculate the code coverage percentage - percentage = round(covered * 100.0 / (missed + covered), 2) - print("Android tests code coverage: %s%%" % (str(percentage))) - - # invoke the script that send the data to s3 - testEnvironment = "LOCAL" - if os.environ.get('IS_LOCAL_DEVELOPMENT').lower()=='false': - testEnvironment = "CI" - - cmd = os.getcwd() + ("/scripts/code-coverage.sh %.2f %s %s" % (percentage, "Android", testEnvironment)) - os.system(cmd) diff --git a/platform/android/scripts/release.py b/platform/android/scripts/release.py deleted file mode 100644 index d0abd1eea..000000000 --- a/platform/android/scripts/release.py +++ /dev/null @@ -1,236 +0,0 @@ -''' -Utility to schedule SDK builds on CircleCI. - -Examples: - -- Publish a snapshot from master (release.py uses the current branch) - - $ git branch - * master - 1234-fix-crash - $ python release.py --stage snapshot - -- Publish a snapshot from a feature branch (same as before, just switch branchs with git): - - $ git branch - master - * 1234-fix-crash - $ python release.py --stage snapshot - -- Publish a beta from a pre-release branch: - - $ git branch - master - * release-android-420-beta1 - $ python release.py --stage beta --version 4.2.0-beta.1 - -- Publish a beta from a release branch: - - $ git branch - master - * release-android-420 - $ python release.py --stage final --version 4.2.0 - -TODO: - -- Add a flag to wait until the release has been built (CircleCI) and published (Maven). - -''' - -import click -import json -import os -import requests -import subprocess - -# Three stages, from less stable to more stable -ALLOWED_STAGES = ['snapshot', 'beta', 'final'] - -# Get the version from GRADLE_PROPERTIES_PATH below -CURRENT_VERSION_TAG = 'current' - -# You can add your API token on https://circleci.com/account/api -CIRCLECI_API_TOKEN_ENV_VAR = 'CIRCLECI_API_TOKEN' - -# In the future we might want to consider alpha, or rc. -ALLOWED_PRE_RELEASE = ['beta'] - -# We get the default version from here -MAPBOX_GL_ANDROID_SDK_PATH = '../MapboxGLAndroidSDK' -GRADLE_PROPERTIES_PATH = '%s/gradle.properties' % MAPBOX_GL_ANDROID_SDK_PATH -GRADLE_TOKEN = 'VERSION_NAME=' - -# Triggers a new build, returns a summary of the build -URL_CIRCLECI = 'https://circleci.com/api/v1.1/project/github/mapbox/mapbox-gl-native/tree/' # + :branch - -# We support three parameters: stage, branch, and version -@click.command() -@click.option('--stage', default=ALLOWED_STAGES[0], type=click.Choice(ALLOWED_STAGES), prompt='Set stage', help='The release stage.') -@click.option('--version', default=CURRENT_VERSION_TAG, prompt='Set version', help='The version you want to publish. E.g: 4.2.0-SNAPSHOT, 4.2.0-beta.1, or 4.2.0. If you set the version to "%s", the script will default to the current SNAPSHOT version.' % CURRENT_VERSION_TAG) -def release(stage, version): - # Validate params - final_stage = validate_stage(stage=stage) - final_branch = validate_branch(stage=final_stage) - final_version = validate_version(stage=final_stage, branch=final_branch, version=version) - - # Get user confirmation - click.echo('\n===== Build information =====') - click.echo('- Stage: %s' % final_stage) - click.echo('- Branch: %s' % final_branch) - click.echo('- Version: %s' % final_version) - click.confirm('\nDoes it look right?', abort=True) - - # Proceed - if (final_stage == 'snapshot'): - publish_snapshot(branch=final_branch, version=final_version) - elif (final_stage == 'beta'): - publish_beta(branch=final_branch, version=final_version) - elif (final_stage == 'final'): - publish_final(branch=final_branch, version=final_version) - -def validate_stage(stage): - if stage not in ALLOWED_STAGES: - abort_with_message('Invalid stage: %s' % stage) - return stage - -def validate_branch(stage): - branch = git_get_current_branch() - if not branch: - abort_with_message('The current folder is not a git repository.') - if branch == 'master' and stage != 'snapshot': - abort_with_message('You need to swtich to a release branch for a beta or a final release.') - return branch - -def validate_version(stage, branch, version): - if stage == 'snapshot' and branch == 'master' and version != CURRENT_VERSION_TAG: - abort_with_message('You cannot specify a custom version if you are building a snapshot from master.') - - if not version or version == CURRENT_VERSION_TAG: - version = get_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN) - - if stage == 'snapshot': - if not 'SNAPSHOT' in version: - abort_with_message('Version should contain the word SNAPSHOT: %s' % version) - elif stage == 'beta': - if not ALLOWED_PRE_RELEASE[0] in version: - abort_with_message('Version should contain the word %s: %s' % (ALLOWED_PRE_RELEASE[0], version)) - elif stage == 'final': - if not version or 'SNAPSHOT' in version or ALLOWED_PRE_RELEASE[0] in version: - abort_with_message('Version cannot be empty, or contain the words SNAPSHOT or %s: %s' % (ALLOWED_PRE_RELEASE[0], version)) - - return version - -def publish_snapshot(branch, version): - click.echo('Publishing snapshot for branch: %s (version: %s).' % (branch, version)) - if branch != 'master': - dirty_gradle = update_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN, version=version) - if dirty_gradle: - git_add(path=GRADLE_PROPERTIES_PATH) - git_commit_and_push(branch=branch, version=version) - do_circleci_request(branch=branch) - -def publish_beta(branch, version): - click.echo('Publishing beta from branch: %s (version: %s).' % (branch, version)) - dirty_gradle = update_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN, version=version) - if dirty_gradle: - git_add(path=GRADLE_PROPERTIES_PATH) - git_commit_and_push(branch=branch, version=version) - do_circleci_request(branch=branch) - -def publish_final(branch, version): - click.echo('Publishing final release from branch: %s (version: %s).' % (branch, version)) - dirty_gradle = update_current_version(file_path=GRADLE_PROPERTIES_PATH, file_var=GRADLE_TOKEN, version=version) - if dirty_gradle: - git_add(path=GRADLE_PROPERTIES_PATH) - if dirty_gradle: - git_commit_and_push(branch=branch, version=version) - do_circleci_request(branch=branch) - -# -# Utils -# - -def abort_with_message(message): - click.echo(message) - click.get_current_context().abort() - -def execute_call(command): - click.echo('Executing: %s' % command) - result = subprocess.call(command, shell=True) - if result != 0: - abort_with_message('Command failed: %s' % command) - -# -# CircleCI -# - -def get_circleci_api_token(): - circleci_api_token = os.environ.get(CIRCLECI_API_TOKEN_ENV_VAR) - if not circleci_api_token: - abort_with_message('You need to set the CIRCLECI_API_TOKEN environment variable.') - click.echo('Found CircleCI API token.') - return circleci_api_token - -def do_circleci_request(branch): - url = URL_CIRCLECI + branch - params = {'circle-token': get_circleci_api_token()} - click.echo('CircleCI request to %s (params: %s)' % (url, json.dumps(params))) - click.confirm('\nDo you want to start a build?', abort=True) - - r = requests.post(url, params=params) - click.echo('- CircleCI response code: %s' % r.status_code) - -# -# Git -# - -def git_get_current_branch(): - return subprocess.check_output('git symbolic-ref --short HEAD'.split(' ')).strip() - -def git_add(path): - execute_call(command='git add %s' % path) - -def git_commit_and_push(branch, version): - message = '[android] [auto] Update properties to version %s in preparation for build.' % version - commands = [ - 'git commit -m "%s"' % message, - 'git push -u origin %s' % branch] - for command in commands: - execute_call(command=command) - -# -# Read and update properties files -# - -def get_current_version(file_path, file_var): - click.echo('Getting current version from %s.' % file_path) - with open(file_path, 'r') as f: - for line in f: - if line.startswith(file_var): - version_name = line[len(file_var):].strip() - click.echo('Current version is %s.' % version_name) - return version_name - return None - -def update_current_version(file_path, file_var, version): - dirty = False - click.echo('Updating file to version %s: %s.' % (version, file_path)) - with open(file_path, 'r') as f: - file_lines = f.readlines() - for line_number in range(len(file_lines)): - if file_lines[line_number].startswith(file_var): - content_old = file_lines[line_number] - content_new = '%s%s\n' % (file_var, version) - if (content_old != content_new): - click.echo('%s -> %s' % (content_old.strip(), content_new.strip())) - file_lines[line_number] = content_new - dirty = True - if dirty: - with open(file_path, 'w') as f: - f.writelines(file_lines) - else: - click.echo('File already has the right version.') - return dirty - -if __name__ == '__main__': - release() diff --git a/platform/android/scripts/run-render-test.py b/platform/android/scripts/run-render-test.py deleted file mode 100644 index 940ae7a3d..000000000 --- a/platform/android/scripts/run-render-test.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/python - -import os -import shutil - -catPath = os.getcwd() + "/platform/android/build/render-test/" -failCounter = 0 -testCounter = 0 -for cat in os.listdir(catPath): - testPath = catPath + cat + "/" - for test in os.listdir(testPath): - inputPath = os.getcwd() + "/mapbox-gl-js/test/integration/render-tests/" + cat + "/" + test - outputPath = testPath + test - - expected = outputPath + "/expected.png" - actual = outputPath + "/actual.png" - output = outputPath + "/output.png" - - shutil.copyfile(inputPath + "/expected.png", expected) - shutil.copyfile(inputPath + "/style.json", outputPath + "/style.json") - pixelmatchCommand = "node_modules/pixelmatch/bin/pixelmatch " + actual + " " + expected + " " + output + " 0.1" - - print "Pixel match " + cat + " " + test - - with os.popen(pixelmatchCommand) as f: - for line_terminated in f: - line = line_terminated.rstrip('\n') - pair = line.split(": ") - if pair[0] == "error": - testCounter += 1 - currentError = pair[1] - if currentError != "0%": - failCounter += 1 - # below deletes successful results - # print cat + " " + test + " " + currentError - # else: - # try: - # if os.path.isdir(outputPath): - # shutil.rmtree(outputPath) - # except OSError, e: - # print ("Error: %s - %s." % (e.filename, e.strerror)) -print -print str(failCounter) + "/" + str(testCounter) diff --git a/platform/android/scripts/validate-license.py b/platform/android/scripts/validate-license.py deleted file mode 100644 index 1c9a153b8..000000000 --- a/platform/android/scripts/validate-license.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/python - -from subprocess import call -from subprocess import Popen, PIPE -import sys - -## Run license generation -call('cd ../../ && make android-license', shell=True) - -## Git diff changes -p = Popen(['git', 'diff', '--name-only', 'LICENSE.md'], stdin=PIPE, stdout=PIPE, stderr=PIPE) -output, err = p.communicate(b"input data that is passed to subprocess' stdin") -if b"platform/android/LICENSE.md" in output: - raise ValueError("""An error ocurred while validating the license generation. - Changes were detected to the license generation output - but weren't commited. Run make android-license and - commit the changeset to make this validation pass.""") \ No newline at end of file diff --git a/platform/android/settings.gradle b/platform/android/settings.gradle deleted file mode 100644 index c0315fed0..000000000 --- a/platform/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':MapboxGLAndroidSDK', ':MapboxGLAndroidSDKTestApp', ':MapboxGLAndroidSDKLint' \ No newline at end of file diff --git a/platform/android/tests/README.md b/platform/android/tests/README.md deleted file mode 100644 index 0d1eb7dea..000000000 --- a/platform/android/tests/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Mapbox GL Android Test documentation - -## Testing -We currently support the following types of testing on the Mapbox Maps SDK for Android: - - - [Unit tests](docs/UNIT_TESTS.md) using [JUnit](http://developer.android.com/tools/testing-support-library/index.html#AndroidJUnitRunner) - - [UI tests](docs/UI_TESTS.md) using [Espresso](http://developer.android.com/tools/testing-support-library/index.html#Espresso) - - [Exerciser](docs/EXERCISER_TESTS.md) tests using [Monkey](http://developer.android.com/tools/help/monkey.html) or [Build-in Fuzz test](http://docs.aws.amazon.com/devicefarm/latest/developerguide/test-types-built-in-fuzz.html) - - [Performance tests](docs/PERFORMANCE_TESTS.md) using [Systrace](https://codelabs.developers.google.com/codelabs/android-perf-testing/index.html?index=..%2F..%2Fbabbq-2015&viewga=UA-68632703-1#0) - - [Core tests](docs/CORE_TESTS.md) using [Google Test](https://github.com/google/googletest) to run the unit tests written for the core rendering component of Mapbox GL \ No newline at end of file diff --git a/platform/android/tests/docs/ACTIVITY_SANITY_TEST.md b/platform/android/tests/docs/ACTIVITY_SANITY_TEST.md deleted file mode 100644 index 7422599c3..000000000 --- a/platform/android/tests/docs/ACTIVITY_SANITY_TEST.md +++ /dev/null @@ -1,17 +0,0 @@ -# Activity Sanity Tests - -This type of test checks if an Activity doesn't crash at start up. For this project this type of test is usefull since a lot of core crashes occurs at this point and are ideal to test regressions. The only run for 1 sec each so there are fast and fairly reliable. We are currently generating the test stack from every Activity found in the following package: - -> com.mapbox.mapboxsdk.testapp - -The generation is done by executing: - -> make test-code-android - -This command underneath executes the following js file found in: - -> node scripts/android-generate-test - -and uses the following ejs file as a template. - ->platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/activity/activity.junit.ejs diff --git a/platform/android/tests/docs/CORE_TESTS.md b/platform/android/tests/docs/CORE_TESTS.md deleted file mode 100644 index b9cd7bcc7..000000000 --- a/platform/android/tests/docs/CORE_TESTS.md +++ /dev/null @@ -1,21 +0,0 @@ -# Core Tests - -Core tests run the [unit tests](../../../../test) written for the core rendering component of Mapbox GL. - -## Running the tests - -To run the tests, connect a device to your computer, or launch a virtual device. Then run - -``` -$ make run-android-core-test-XXX -``` - -with `XXX` being the architecture of the device (one of `arm-v5`, `arm-v7`, `arm-v8`, `x86`, `x86-64`, or `mips`). This command first builds the library, creates a test program, zips up the assets required for the unit tests, deploys it on the device, runs the test and unpacks the results again on your computer. - -You can also run individual tests by specifying the name like this: - -``` -$ make run-android-core-test-XXX-TileCover.* -``` - -Everything after the last dash is passed as the [`--gtest_filter` argument](https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#running-a-subset-of-the-tests) to the Google Test binary. diff --git a/platform/android/tests/docs/EXERCISER_TESTS.md b/platform/android/tests/docs/EXERCISER_TESTS.md deleted file mode 100644 index 4de56912a..000000000 --- a/platform/android/tests/docs/EXERCISER_TESTS.md +++ /dev/null @@ -1,23 +0,0 @@ -#UI/Application Exerciser Tests - -UI/application exerciser tests are stress test using random generated users events. - -##Running Locally - -The Android SDK provides a test tool called [Monkey](http://developer.android.com/tools/help/monkey.html), -"a program that runs on your emulator or device and generates pseudo-random streams of user events -such as clicks, touches, or gestures, as well as a number of system-level events." - -To exercise Monkey on the test app, install the package on the device (e.g. via Android Studio) -and then: - -``` -$ adb shell monkey -p com.mapbox.mapboxgl.testapp -v 500 -``` - -##Running on AWS Device Farm - -Amazon Device farm supports a similar tool called `Built-in Fuzz Test`. -"The built-in fuzz test randomly sends user interface events to devices and then reports results." - -More information about [Built-in Fuzz Test](http://docs.aws.amazon.com/devicefarm/latest/developerguide/test-types-built-in-fuzz.html) \ No newline at end of file diff --git a/platform/android/tests/docs/PERFORMANCE_TESTS.md b/platform/android/tests/docs/PERFORMANCE_TESTS.md deleted file mode 100644 index a182af826..000000000 --- a/platform/android/tests/docs/PERFORMANCE_TESTS.md +++ /dev/null @@ -1,69 +0,0 @@ -# Performance Tests - -## What is Systrace? - -From the [Android documentation](http://developer.android.com/tools/help/systrace.html): -"Systrace is a tool that will help you analyze the performance of your application by capturing and displaying execution times of your applications processes and other Android system processes." - -## What are we using it for? - -We’re using Systrace to look for performance issues in our SDK. When we run Systrace while using our app, -it will collect data from Android then format it into an html file we can view in a web browser. -When we open the Systrace results, we’re hoping it will be able to help us resolve some of the -underlying issues associated with our SDK. - -## Run systrace locally - -Execute following command in your terminal will run Systrace for 10 seconds: - -``` -python $ANDROID_HOME/platform-tools/systrace/systrace.py --time=10 -o ~/trace.html gfx view res -``` - -This command will output a file called `trace.html`. -More information how to interpret the values can be found [here](http://developer.android.com/tools/help/systrace.html). - -More information about this topic can be found [here](https://codelabs.developers.google.com/codelabs/android-perf-testing/index.html?index=..%2F..%2Fbabbq-2015&viewga=UA-68632703-1#0) - -## Automating Systrace with Espresso - -The following annotation is being used to isolate the tests and classes that should be included in the performance tests. - -```java -@PerfTest -``` - -You can optionally define extra rules to gather more data during a performance test: - -```java -@Rule -public EnableTestTracing mEnableTestTracing = new EnableTestTracing(); - -@Rule -public EnablePostTestDumpsys mEnablePostTestDumpsys = new EnablePostTestDumpsys(); - -@Rule -public EnableLogcatDump mEnableLogcatDump = new EnableLogcatDump(); - -@Rule -public EnableNetStatsDump mEnableNetStatsDump = new EnableNetStatsDump(); -``` - -## Automating Systrace with MonkeyRunner - -An example of such a script can be found [here](https://github.com/googlecodelabs/android-perf-testing/blob/master/run_perf_tests.py). -The script is following this structure: - -- Check environment variables -- Define functions -- Clear local data from previous runs -- Find an Android device -- Enable and clear graphics info dumpsys -- Start a systrace thread & test suite thread in parallel -- Wait for both threads to complete -- Download files from device -- Run analysis on downloaded files - -## Note -Testing on a device with minimum SDK 6.0 is preferred. -The tools above work on older versions of Android, but less data will be collected. \ No newline at end of file diff --git a/platform/android/tests/docs/UI_TESTS.md b/platform/android/tests/docs/UI_TESTS.md deleted file mode 100644 index 1014b5684..000000000 --- a/platform/android/tests/docs/UI_TESTS.md +++ /dev/null @@ -1,67 +0,0 @@ -#UI Tests -## Running Espresso tests locally - -This test project comes with all the required Android Testing Support Library dependencies -in the Gradle file. Tests are under the `app/src/androidTest` folder. - -Note that before running your tests, you might want to turn off animations on your test device. -It's a known issue that leaving system animations turned on in a test device -(window animation scale, transition animation scale, animator duration scale) -might cause unexpected results, or may lead tests to fail. - -To create a new run configuration: -* Click on Run -> Edit Configurations... -* Click on the plus sign and then on "Android Tests" -* Give a name to the configuration, e.g. `TestAppTests` -* Choose the `MapboxGLAndroidSDKTestApp` module -* Choose `android.support.test.runner.AndroidJUnitRunner` as the instrumentation runner -* Click OK to save the new configuration - -You can now run this configuration from the main toolbar dropdown menu. - -## Running Espresso tests on AWS Device Farm - -On a terminal, within `mapbox-gl-native/android/java`, -run the tests (`cC` stands for `connectedCheck`): - -``` -$ ./gradlew -Pmapbox.abis=all cC -p MapboxGLAndroidSDKTestApp -``` - -Then: -* Go to your AWS Console and choose Device Farm. -* Create a new project, e.g. `MapboxGLAndroidSDKTestApp` -* On step 1, upload the APK in `mapbox-gl-native/platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-unaligned.apk` -* On step 2, choose Instrumentation, test filter is `com.mapbox.mapboxgl.testapp.MainActivityTest` and upload the APK in `mapbox-gl-native/platform/android/MapboxGLAndroidSDKTestApp/build/outputs/apk/MapboxGLAndroidSDKTestApp-debug-androidTest-unaligned.apk` -* On step 3, choose a device pool. E.g. Top Devices -* On step 4, customize your device state (if needed) -* Finally, confirm the configuration and run the tests. - -On Step 2, you can also separate by commas different classes: `com.mapbox.mapboxgl.testapp.MainActivityTest,com.mapbox.mapboxgl.testapp.MainActivityScreenTest` - -If you have no tests for your app, or want to test some random user behavior, -you can just choose "Built-in: Fuzz" in step 2. - -### Code coverage -You can generate JaCoCo reports from espresso tests by - -- adding this to build.gradle: - - ```java - buildTypes { - debug { - // Run code coverage reports by default on debug builds. - testCoverageEnabled = true - } -} -``` - -- running the gradle task `createMockDebugCoverageReport` when executing tests. - -## Running Espresso test automatically on AWS Device Farm -To run tests on AWS device farm you need to execute `./gradlew -Pmapbox.abis=none devicefarmUpload`. -You can configure the different steps in the testapp `build.gradle`. -AWS credentials are found in CircleCI. - - - diff --git a/platform/android/tests/docs/UNIT_TESTS.md b/platform/android/tests/docs/UNIT_TESTS.md deleted file mode 100644 index 458e8869f..000000000 --- a/platform/android/tests/docs/UNIT_TESTS.md +++ /dev/null @@ -1,92 +0,0 @@ -# Unit tests -**Unit testing is a software development process in which the smallest testable parts of an application, called units, are individually and independently scrutinized for proper operation.** - -Our Unit tests are based on JUnit and are located under `/src/test/java/`. -We are using plain JUnit to test classes that aren't calling the Android API, -or are using Android's JUnit extensions to stub/mock Android components. - -## Writing unit tests -Unit tests for an Android project are located in the `test` folder: - -screen shot 2016-03-16 at 17 24 31 - -To add a test, right click the corresponding package and select create new `Java class`. Name the class appropriately by ending it with `Test` and start adding test methods by applying the `@Test` annotation: - -```java -package com.mapbox.mapboxsdk.annotations; - -import org.junit.Test; - -public class AwesomeSauceTest { - - @Test - public void simpleTest(){ - - } - -} - -``` - -you can add methods that are executed before and after each test: - -```java -package com.mapbox.mapboxsdk.annotations; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -public class AwesomeSauceTest { - - @Before - public void beforeEachTest(){ - - } - - @Test - public void simpleTest(){ - - } - - @After - public void afterEachTest(){ - - } - -} -``` - -## Running unit tests -You can run a test locally by right clicking and selecting run: - -screen shot 2016-03-16 at 17 46 09 - -If you like, you can also run with test coverage enabled. This will show you the following dialog: - -screen shot 2016-03-16 at 17 58 34 - -You can also run the tests from the command line with: - -``` -$ ./gradlew -Pmapbox.abis=none test -p MapboxGLAndroidSDKTestApp -``` - -## Running Unit tests on CI -The Unit tests are executed as part of the build process on our CI and are -automatically run for each new commit pushed to this repo. If a Unit tests -fails, this will fail and stop the build. - -You can find this gradle command in our [buildscript](https://github.com/mapbox/mapbox-gl-native/blob/master/circle.yml#L146-L215): - -``` -$ ./gradlew -Pmapbox.abis=none testReleaseUnitTest -``` - - -## Code Coverage -Showing code coverage directly in the IDE. -- Switch your Build Variant to the Unit Tests artifact -- Right click a unit test and select `Run test with coverage` -- Select `Add to active suites` //this will create a run configuration -- Edit the run configuration to include/exclude packages in the `Code coverage`-tab. diff --git a/platform/android/tests/skipped.txt b/platform/android/tests/skipped.txt deleted file mode 100644 index cf2865683..000000000 --- a/platform/android/tests/skipped.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Flaky -# After map is rendered, main thread hangs in runloop::wake when data is written to pipe -# Issue: https://github.com/mapbox/mapbox-gl-native/issues/13711 -Memory.Raster -Memory.Vector - -# Exception is thrown when network status is changed to online -# Issue: https://github.com/mapbox/mapbox-gl-native/issues/13711 -Map.Offline diff --git a/platform/android/vendor/mapbox-events-android b/platform/android/vendor/mapbox-events-android deleted file mode 160000 index 58fbec16c..000000000 --- a/platform/android/vendor/mapbox-events-android +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 58fbec16cdedb909eb63c68e46708fe9268b6fed diff --git a/platform/android/vendor/mapbox-gestures-android b/platform/android/vendor/mapbox-gestures-android deleted file mode 160000 index ae08de82c..000000000 --- a/platform/android/vendor/mapbox-gestures-android +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ae08de82ca178dca7f005dfc8056554cf77124ad diff --git a/platform/android/vendor/mapbox-java b/platform/android/vendor/mapbox-java deleted file mode 160000 index f590c74df..000000000 --- a/platform/android/vendor/mapbox-java +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f590c74df50f1b1e8edf2a4cf4908701b85861f2 diff --git a/platform/android/version-script b/platform/android/version-script deleted file mode 100644 index 7c1e3d54a..000000000 --- a/platform/android/version-script +++ /dev/null @@ -1,4 +0,0 @@ -{ - global: JNI_OnLoad; - local: *; -}; \ No newline at end of file